Docker で Laravel 環境構築

# はじめに
私は2023年10月より、内定直結型エンジニア学習プログラム「アプレンティス」に2期生として参加しています。

[【働きながら学べる】エンジニア実習 アプレンティス](https://apprentice.jp/)

カリキュラムの中でチーム開発があり、Docker での Laravel 環境構築を担当することになったので、備忘録としてまとめます。

## 目次
[1.導入するもの](#1-導入するもの)
[2.ディレクトリ構成](#2-ディレクトリ構成)
[3.compose.yaml](#3-compose.yaml)
[4.追加設定](#4-追加設定)
[5.ガントチャートによる進捗管理](#5-ガントチャートによる進捗管理)
[6.タスク管理機能の実装](#6-タスク管理機能の実装)
[7.デイリポ　機能の紹介](#7-デイリポ　機能の紹介)
[8.振り返り](#8-振り返り)


# 1. 導入するもの
今回は PHP のフレームワークである Laravel の学習期間ということで、Laravel で開発ができるように環境を整えます。
今回は PHP と Apache がセットになったイメージを採用しました。

併せて、静的解析ツールでのコードのリファクタリングや、デバッグができるように各種ツールも導入します。

フロント側は Vanilla JavaScript(生のJavaScriptのこと)を使用することを想定し、こちらの静的解析ツールも併せて導入します。
(※この後 React を学ぶカリキュラムとなっているため、その際に React も使用できるようにアップデートする予定です。)

データベースは MySQL を使用します。ターミナルで実行することを想定し、PHP My Admin は導入しません。
データベース設計を視覚化するために、ER図をVSCode上で作成できるツールを導入します。

MySQLへ接続する際のパスワードなどをコードにベタ書きせず環境変数として呼び出したいため、dotenvファイルを導入します。

言語
- Laravel (PHP)

デーテベース
- MySQL

パッケージ管理システム
- Composer
- npm

テスト・デバッグ・静的解析ツール 等
- PHP Code Sniffer
- PHP Stan
- PHP MD
- Xdebug
- ES Lint
- Prettir
- W3C Web Validator  

ER図 描画ツール
- Plant UML

環境変数管理ツール
- dotenv

# 2. ディレクトリ構成
```
```

# 3. compose.yaml
以前は `docker-compose.yml` として定義されてきたファイルですが、現在はファイル名を `compose.yaml` とすることが推奨されているようです。
```yml
services:
  app:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    ports:
      - "50080:80"
    volumes:
      - ./src:/var/www/html
    depends_on:
      - db

  db:
    build:
      context: .
      dockerfile: docker/db/Dockerfile
    ports:
      - "53306:3306"
    volumes:
      - ./docker/db/my.cnf:/etc/mysql/my.cnf
      - ./docker/db/mysql_data:/var/lib/mysql
      - ./docker/db/csv_data:/var/lib/mysql-files/
    env_file:
      - ./docker/db/db-variables.env

  plantuml-server:
    image: plantuml/plantuml-server
    container_name: plantuml-local-server
    ports:
      - "8080:8080"
```
上記ファイルの記載内容について説明していきます。

`service`：各コンテナのことを service と呼びます。
この例の場合、service からひとつ分インデントを下げた位置にある `app`, `db`, `plantnml-server` の3つが service (コンテナ)名です。コンテナ名は自由に決めることができます。
app コンテナはアプリケーションのメインのコンテナで、Apache サーバー上で PHP が実行できるので、Laravel などのファイルを作成します。
db コンテナはデータベース用のコンテナで、MySQLに接続します。
plantuml-server は、ER 図を作成するツールである Plant UML を利用するためのサーバーです。
`docker-compose up -d`（コンテナを起動するコマンド）を実行すると、この3つのコンテナが立ちあがるということになります。

`build`：`context`の後の`.`は、compose.yamlファイルがあるディレクトリ（この場合はカレントディレクトリ）を指定しています。`dockerfile`は、該当コンテナの Dockerfile までのパスを指定します。

app　コンテナの`ports`では、ホスト（ローカルのPC）の 50080 番ポートへの通信を、コンテナの 80 番ポートに繋ぐ設定をしています。Apache は 80 番ポートを使用します。ブラウザから localhost:50080 の URL で接続できます。
db コンテナの MySQL は 3306 番ポートを使用するため、そのように指定します。
plantuml-server コンテナは、ブラウザから localhost:８０８０ の URL で接続することで、コードを入力するとER図を描画することができます。

`volumes`は、ローカルPCの ./src ディレクトリ以下を、 app コンテナの
/var/www/html ディレクトリ以下にマウントしています。ディレクトリが同期されるという意味です。これによって、コンテナを削除してもデータはローカルPCに残るため、データを永続化することができます。

`depends_on`は、依存関係を定義してコンテナの起動順序を制御する設定です。
この場合、db コンテナが起動してから、app コンテナが起動する流れになります。
db コンテナが予め起動して接続を受け付けていないと、app コンテナがエラーになり起動に失敗することがあるらしく、このように設定します。

参考[【Docker Compose】depends_onについて](https://qiita.com/penpen22/items/dfb430be0f7d7478a5cd)

`env_file`は、環境変数の設定ファイルのパスを指定しています。

`image`は、plantuml-serverコンテナのベースとなるイメージです。

## docker ディレクトリ
docker ディレクトリには、コンテナ毎に必要なファイルをまとめます。

### docker/app/Dockerfile
app コンテナの Dockerfile です。
```dockerfile
# ベースイメージ
FROM php:8-apache

# 作業ディレクトリ
WORKDIR /var/www/html

# PHP ライブラリ
RUN apt-get update \
    && apt-get install -y nodejs npm libonig-dev libzip-dev unzip mariadb-client default-jre \
    && docker-php-ext-install mbstring zip bcmath pdo_mysql mysqli \
    && pecl install xdebug \
    && docker-php-ext-enable xdebug

# JavaScript ライブラリ
RUN npm install -D eslint prettier eslint-config-prettier

# mod_rewrite モジュールを使えるように（有効化）する
# Apache 側で要求された URL を書き換えたり、リダイレクトしたりできる
# これをしないと Laravel でルート以外にアクセスできないらしい
RUN a2enmod rewrite

# composer のインストール
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# 環境変数の設定（root での Compoer インストールを許可する）
ENV COMPOSER_ALLOW_SUPERUSER 1

# 設定ファイルをローカルで作成した上で、docker イメージで使えるようにする
# 相対パスを指定した場合、ホスト側はビルドコンテキスト、コンテナ側は WORKDIR をカレントディレクトリとする
# ローカルの ./src ディレクトリを docker イメージの /var/www/html ディレクトリにコピーする
COPY ./src /var/www/html
# php.ini ファイルをコピーする
COPY ./docker/app/php.ini /usr/local/etc/php/php.ini
# Xdebubgの設定ファイルをコピーする
COPY ./docker/app/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
```
詳しく解説していきます。
```dockerfile
FROM php:8-apache
```
appコンテナのベースイメージです。
今回は、PHPとApacheがセットになったイメージを使用しています。

```dockerfile
WORKDIR /var/www/html
```
プロジェクトのルートディレクトリを指定しています。
/var/www/html は Apache のデフォルトのファイル置き場です。

```dockerfile
RUN apt-get update \
```
`RUN` は、Dockerイメージを作成するときに実行する内容を指定するコマンドです。
`apt-get update`で、apt(Linuxでソフトウェアの導入や管理、削除に用いられるパッケージ管理システムの一つ)のアップデートを行います。
末尾の`\`は、次の行に続くことを表しています。

```dockerfile
&& apt-get install -y nodejs npm libonig-dev libzip-dev unzip mariadb-client default-jre \
 ```
 `&&`で、前の行から続いていることを表します。
 `apt-get install` で、必要なライブラリをインストールします。
 `-y`オプションは、インストールの際にYes/Noの選択肢がある場合ストップしないように、全てYesで進むように指定します。

 以下、インストールする内容です。
 `nodejs`: JavaScriptのサーバーサイド実行環境である Node.js。
 `npm`: Node.jsのパッケージ管理システム。
 `ibonig-dev`: 正規表現ライブラリの開発ファイル。PHP 7.4 以降で必要。
 `libzip`: PHP のzip拡張機能の有効化に必要なライブラリー。
 `unzip`: Linux 環境で zip を解凍する unzip コマンドを使えるようにする。
 `mariadb-client`: MariaDB のクライアントアプリケーションである mysql を使えるようにするパッケージ。
`default-jre`: Java の実行環境。Plant UML が Java で動作するツールのため必要。


```dockerfile
 && docker-php-ext-install mbstring zip bcmath pdo_mysql mysqli \
 ```
PHP拡張モジュールのインストール。
 `mbstring`: PHP 上でマルチバイト文字(全角文字)を扱えるようにする。
 `zip`: ZIP 圧縮されたアーカイブとその内部のファイルに対する透過的な読み書きが可能になる。
 `bcmath`: 小数点の計算誤差対策の BCMath関数を使えるようにする。
 `pdo_mysql`: PHP から MySQL データベースへのアクセスを可能にする PHP Data Objects (PDO) インターフェイス を実装したドライバ。
 `mysqli`: 改良版拡張モジュール。MySQL 4.1 以上の機能を利用できる。
 
 ```dockerfile
 && pecl install xdebug \
 && docker-php-ext-enable xdebug
 ```
 PHP のデバッグツールである Xdebub のインストールと有効化。

```dockerfile
RUN npm install -D eslint prettier eslint-config-prettier
```
 JavaScript の静的解析ツールである ES Lint と Prettier のインストール。-D オプションは開発時のみ使用したい場合につける。

```dockerfile
RUN a2enmod rewrite
```
mod_rewrite モジュールを使えるように（有効化）する。
有効化することで、Apache 側で要求された URL を書き換えたり、リダイレクトしたりできる。

```dockerfile
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
```
PHP のパッケージ管理ツールである Composer のインストール。

```dockerfile
ENV COMPOSER_ALLOW_SUPERUSER 1
```
root での Composer インストールを許可するための設定

```dockerfile
COPY ./src /var/www/html
```
設定ファイルをローカルで作成した上で、それをコピーして docker イメージで使えるようにする。
相対パスを指定した場合、ホスト側はビルドコンテキスト、コンテナ側はWORKDIR をカレントディレクトリとする。
ローカルの ./src ディレクトリを docker イメージの /var/www/html ディレクトリにコピーしている。

```dockerfile
COPY ./docker/app/php.ini /usr/local/etc/php/php.ini
COPY ./docker/app/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
```
 php.ini ファイル、Xdebubgの設定ファイルをそれぞれコピーする。

### docker/app/php.ini
PHPの設定ファイル
```ini
display_errors = On
display_startup_errors = On
max_execution_time = 30
session.gc_divisor = 1000
session.sid_bits_per_character = 5
output_buffering = 4096
memory_limit = 128M
upload_max_filesize = 64M
post_max_size = 64M
max_input_vars = 1000
log_errors = On
error_log = /dev/stderr
error_reporting = E_ALL
expose_php = On
zend.exception_ignore_args = Off

[Date]
date.timezone = "Asia/Tokyo"

[mbstring]
mbstring.language = Japanese

[mysqlnd]
mysqlnd.collect_memory_statistics = On

[assertion]
zend.assertions = 1
```
詳しく説明していきます。
```ini
display_errors = On
```
エラーをHTML出力の一部として画面に出力するかどうか。開発環境ではonにします。

```ini
display_startup_errors = On
```
PHPの起動シーケンスにおいて発生したエラーの表示。

```ini
max_execution_time = 30
```
スクリプトの最大実行時間を秒で指定する。
デフォルトは 60 で、長く設定するとDoS攻撃のリスクを高める。

```ini
session.gc_divisor = 1000
```
セッションの保持期間の指定。
24分より古くなったら、1/1000の確率でGC(ガーベッジコレクション)が動き削除を行う(デフォルト1000)。

```ini
session.sid_bits_per_character = 5
```
セッションIDに使用する文字種を指定（デフォルトは4：16進数 0~9a~f）。
セキュリティ的に 5 が（0~9a~v）が推奨されている。

```ini
output_buffering = 4096
```
出力バッファリング。
出力結果を一旦バッファに溜め込む機能＝実行速度の向上の効果（デフォルト4096 byte）。

```ini
memory_limit = 128M
```
PHPが使用可能なメモリの上限を指定する。

```ini
upload_max_filesize = 64M
post_max_size = 64M
```
ファイルアップロード上限サイズをアップ。

```ini
max_input_vars = 1000
```
1 リクエストで受け付ける最大の入力変数の数(デフォルト1000)。

```ini
log_errors = On
```
エラーメッセージを、サーバーのエラーログまたは error_log に記録するかどうか。

```ini
error_log = /dev/stderr
```
スクリプトエラーが記録されるファイル名。

```ini
error_reporting = E_ALL
```
エラー出力レベルを設定 E_ALL は 全ての PHP エラーを表示する(開発環境ではon)。

```ini
expose_php = On
```
有効にするとHTTPヘッダに X-Powered-By: PHP/7.4.5 とPHPのバージョン情報が表示される(開発環境ではon)。

```ini
zend.exception_ignore_args = Off
```
有効にすると例外のスタックトレースに引数情報が出なくなる(開発環境ではoff)。

```ini
[Date]
date.timezone = "Asia/Tokyo"
```
timezoneを日本時間に指定する。

```ini
[mbstring]
mbstring.language = Japanese
```
mbstring：PHPでマルチバイト文字（全角）を使用できるようにする、PHPの拡張モジュール。
mbstringで使用するデフォルトの言語を日本語にする。

```ini
[mysqlnd]
mysqlnd.collect_memory_statistics = On
```
さまざまなメモリ統計情報の収集を有効にする。

```ini
[assertion]
zend.assertions = 1
```
アサーション：「ここではこういった入力・結果が true である」という前提条件をコード内に記述すること。
1：アサーションのコードを生成して実行 (開発環境では on = 1 )。
テストの実行結果が想定通りの結果となっているか確認するためのメソッド（失敗するとしたらバグがある）。


### docker/app/xdebug.ini
Xdebug の設定ファイル
```ini
xdebug.client_host = localhost
xdebug.start_with_request = yes
xdebug.mode = debug
xdebug.client_port = 9003
xdebug.log = /var/log/xdebug.log
```
解説します。

```
xdebug.client_host = localhost
```
呼び出し元のIPアドレス。
今回 Xdebug はdockerコンテナ（同じコンテナ）上で実行するので localhost を指定。

```
xdebug.start_with_request = yes
```
PHPのリクエスト開始時に Xdebug を有効にする。

```
xdebug.mode = debug
```
ステップ実行ができるデバッグモードを指定。

```
xdebug.client_port = 9003
```
Xdebug に接続する際に使われるポート番号。
デフォルトは9003なので、9003なら本来は指定しなくてもいい。
今回は明示的に記載。

```
xdebug.log = /var/log/xdebug.log
```
ログファイルのパス。

### docker/db/Dockerfile
db コンテナの Dockerfile
```dockerfile
FROM mysql:8

RUN microdnf install yum \
  && yum install -y glibc-langpack-ja glibc-locale-source git

ENV LANG ja\_JP.UTF-8
```

解説します。

```dockerfile
FROM mysql:8
```
MySQL のベースイメージを取得。

```dockerfile
RUN microdnf install yum \
```
Linux のパッケージ管理ツールの Yum をインストール。

```dockerfile
&& yum install -y glibc-langpack-ja glibc-locale-source git
```
My SQL がデフォルトで日本語に対応していないので、文字化け対応として、Yum を使用してライブラリをインストールする。

```
ENV LANG ja\_JP.UTF-8
```
環境変数で言語を日本語を指定する。


### docker/db/my.cnf
```ini
[client]
default-character-set=utf8mb4
```
データベースのデフォルトの文字を utf8mb4（日本語対応）にする。

## Dev Container の設定ファイル
Xdebug を使ってデバッグするためには、Dev Container （旧 Remote Development）を使う必要があります。
その設定をルートディレクトリ直下の .devcontainer ディレクトリ内に作った devcontainer.json ファイルに記載します。

.devcontainer/devcontainer.json
```json-doc
{
  "name": "apprentice_dev2",
  "dockerComposeFile": ["../docker-compose.yml"],
  "service": "app",
  "workspaceFolder": "/var/www/html",
  "forwardPorts": [9003],
  "customizations": {
    "extensions": [
      "ecmel.vscode-html-css",
      "esbenp.prettier-vscode",
      "xdebug.php-debug",
      "bmewburn.vscode-intelephense-client",
      "oderwat.indent-rainbow",
      "streetsidesoftware.code-spell-checker",
      "IBM.output-colorizer",
      "formulahendry.auto-rename-tag",
      "CelianRiboulet.webvalidator",
      "dbaeumer.vscode-eslint",
      "onecentlin.laravel-blade",
      "onecentlin.laravel5-snippets",
      "ryannaddy.laravel-artisan",
      "amiralizadeh9480.laravel-extra-intellisense",
      "mikestead.dotenv",
      "shufo.vscode-blade-formatter",
      "IHunte.laravel-blade-wrapper",
      "jebbs.plantuml"
    ]
  }
}
```
解説します。
```json
 "name": "apprentice_dev2"
```
リモートコンテナの名前。

```json
"dockerComposeFile": ["../docker-compose.yml"]
```
docker-compose.yml ファイルのパスを指定。

```json
"service": "app"
```
対象のコンテナを指定

```json
"workspaceFolder": "/var/www/html",
```
ワークスペースを指定。

```json
"forwardPorts": [9003],
```
コンテナ内からローカルに転送するポートを指定する。
Xdebug は 9003 を使用。

```json
"extensions": [
```
拡張機能を Dev Container 内で使えるようにする。
以下、拡張機能の一覧。

`ecmel.vscode-html-css`：HTML,CSS のインテリセンス

`esbenp.prettier-vscode`：prettier

`xdebug.php-debug`：Xdebug

`bmewburn.vscode-intelephense-client`：PHPコード補完やチェック

`oderwat.indent-rainbow`：インデントレインボー

`streetsidesoftware.code-spell-checker`
`IBM.output-colorizer`：コードスペルチェック

`formulahendry.auto-rename-tag`：タグ補完

`CelianRiboulet.webvalidator`：W3C バリデーター

`dbaeumer.vscode-eslint`：ESLint

`onecentlin.laravel-blade`：Laravel Blade スニペット

`onecentlin.laravel5-snippets`：Laravel スニペット

`ryannaddy.laravel-artisan`：Laravel artisan 便利機能

`amiralizadeh9480.laravel-extra-intellisense`：Laravel コード補完

`mikestead.dotenv`：.envファイルの文法ハイライト

`shufo.vscode-blade-formatter`：Laravel Blade フォーマッター

`IHunte.laravel-blade-wrapper`：Laravel Blade wrapper

`jebbs.plantuml`：Plant UML

## Git 管理下に置かないファイル
パスワードが書かれた .env ファイルや、 vendor（インストールしたライブラリなどが入ったディレクトリ）は GitHub リポジトリに上げないため、Gitが無視するようにこのファイルに記載する。

.gitignore
```
docker/db/mysql_data
src/vendor
.env
```

## コンテナの起動
これらを作成した後、compose.yaml があるディレクトリ(プロジェクトルート)で下記コマンドを実行します。
`docker-compose up -d`
イメージがビルドされ、コンテナが立ち上がります。

## Laravel のインストール
下記コマンドを実行することで、プロジェクトディレクトリが作成されます。
`composer create-project laravel/laravel --prefer-dist <プロジェクト名>`

## 簡易サーバーの起動
下記コマンドを実行。
`php artisan serve`

localhost:8000 にアクセスし、Laravel が表示されれば成功です。

## まとめ
ここまで、Docker における Laravel の導入をまとめてみました。
実際に開発するにはまだ必要な設定がありますが、また改めて解説しようと思います。
Dockerの挙動はまだ理解できていない部分も多いので、使いながら学んで行きたいと思います。