Dockerを使ってLaravel(php-fpm + mariadb + nginx)  の環境を構築したい！

# はじめに
大学のプロジェクトで環境をDockerに移動しなければいけない機会があったので，Laravelの環境を作成しました！
コンテナ内のartisanコマンド等で作成されたファイルは権限を変更しないとVScodeで直接編集でしたが、今回はVScodeで直接編集ができるように工夫しました！

# やりたいこと
php-fpm + mariadb + nginx のLaravelの環境を作成したい！

# お急ぎの方
以下のリポジトリをForkして作成したリポジトリをcloneしてください！
※ .envファイルを記述する必要あり．

https://github.com/46maru/Laravel_container

# 動作環境
- windows10
- wsl2
- VScode
- docker for windows (4.30.0)

# ファイル構成
```
project名
    ┣━ docker/
    ┃   ┣━ app/
    ┃       ┣━ Dockerfile
    ┃       ┣━ php.ini
    ┃   ┣━db/
    ┃       ┣━ Dockerfile
    ┃       ┣━ my.conf
    ┃   ┣━web/
    ┃       ┣━ Docerfile
    ┃       ┣━ default.conf
    ┃
    ┣━ src/
    ┃ 　┣━ Laravelのファイル群
    ┣━ .env
    ┣━ .gitignore
    ┣━ docker-compose.yml
```

# 手順

### 1. 各種ファイル・フォルダを作成する．
ファイル構成と各種ファイル詳細を参考にしてファイルとフォルダを作成してください．
### 2. コンテナの立ち上げ

#### 2-1. 現在のコンテナを確認
```
$ docker-compoes ps
NAME      IMAGE     COMMAND   SERVICE   CREATED   STATUS    PORTS
```

#### 2-2. イメージの作成
```
$ docker-compose build
```

#### 2-3. コンテナの起動
```
$ docker-compose up -d
[+] Running 4/4
 ✔ Network laravel_container_Laravel_network  Created                                                                                                                                                                             0.0s 
 ✔ Container laravel_container-db-1           Started                                                                                                                                                                             0.8s 
 ✔ Container laravel_container-app-1          Started                                                                                                                                                                             0.8s 
 ✔ Container laravel_container-web-1          Started  
```

#### 2-3. 起動したコンテナを確認
```
$ docker-compoes ps
NAME                      IMAGE                   COMMAND                  SERVICE   CREATED         STATUS         PORTS
laravel_container-app-1   laravel_container-app   "docker-php-entrypoi…"   app       3 minutes ago   Up 3 minutes   9000/tcp
laravel_container-db-1    laravel_container-db    "docker-entrypoint.s…"   db        3 minutes ago   Up 3 minutes   0.0.0.0:3306->3306/tcp
laravel_container-web-1   laravel_container-web   "/docker-entrypoint.…"   web       3 minutes ago   Up 3 minutes   0.0.0.0:8081->80/tcp
```

### 3. コンテナ内でLaravelプロジェクトの作成(
※ 今回はLaravel10.xのプロジェクトを作成します．

#### 3-1. コンテナの中に入る
```
$ docker-compose exec app bash
```

#### 3-2. コンテナ内でLaravelプロジェクトの作成
```
$ composer create-project laravel/laravel . "10.*" --prefer-dist
```

#### 3-3. Laravelプロジェクトが作成されているか確認
```
$ ls
README.md  app  artisan  bootstrap  composer.json  composer.lock  config  database  package.json  phpunit.xml  public  resources  routes  storage  tests  vendor  vite.config.js
```

#### 3-4. プレビューを確認
[localhost:8081](http://localhost:8081)にアクセスして以下の画面のようになっているか確認してください。

![アプリ起動画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2949762/c79719c2-a38c-0388-a300-ba0f0b39c55a.png)


### 4. データベースとの接続

#### 4-1. Laravelの.envファイルの編集
srcフォルダ内にある.envファイルを編集します．
※ コンテナを作成する際に指定した.envファイルとは異なります．
``` /src/.env
DB_CONNECTION=mysql
DB_HOST=db #127.0.0.1から変更
DB_PORT=3306 
DB_DATABASE=接続するDBの名前 #コンテナを作成したときに.envファイルで指定したものを記述．
DB_USERNAME=ユーザー名 #コンテナを作成したときに.envファイルで指定したものを記述．
DB_PASSWORD=パスワード #コンテナを作成したときに.envファイルで指定したものを記述．
```

#### 4-2. データベースのアクセス権限の設定
まず，dbコンテナの中に入ります．
```
$ docker-compose exec db bash
```

次に，MariaDBの中のmysqlテーブルに入ります．
```
$ mariadb -u root -p mysql
```

最後に，でアクセス権限の設定を行います．
```
$ GRANT ALL PRIVILEGES ON データベース名.* TO 'ユーザー名'@'%';
```

#### 4-3. migrationの実行
データベースとの接続を確認をするためにmigrationを実行します．
まず，再びappコンテナの中に入ります．
```
$ docker-compose exec app bash
```
次に，コンテナの中でmigrationを実行します．
```
$ php artisan migrate
```
migrationが上手くいけば成功です！

### ※ 補足(コンテナ停止・再開・再起動)

#### コンテナの停止
```
$ docker-compose stop
```

#### コンテナの再開
```
$ docker-compose start
```

#### コンテナの再起動
```
$ docker-compose restart
```

# 各種ファイル詳細

## APP(php-fpm)

### Dockerfile
``` Docker:docker/app/Dockerfile
FROM php:8.2-fpm

ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN groupadd --gid $USER_GID $USERNAME \
    && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \
    && apt-get update \
    && apt-get install -y sudo \
    && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
    && chmod 0440 /etc/sudoers.d/$USERNAME \
    && apt-get clean

RUN apt-get update \
    && apt-get install -y \
    git \
    zip \
    unzip \
    vim \
    libfreetype6-dev \
    libjpeg62-turbo-dev \
    libmcrypt-dev \
    libpng-dev \
    libfontconfig1 \
    libxrender1

RUN docker-php-ext-configure gd --with-freetype --with-jpeg
RUN docker-php-ext-install gd
RUN docker-php-ext-install bcmath
RUN docker-php-ext-install pdo_mysql mysqli exif
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer

USER $USERNAME

ENV NODE_VERSION=18.17.0
ENV NVM_DIR=/home/$USERNAME/.nvm
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash \
    && . "$NVM_DIR/nvm.sh" \
    && nvm install ${NODE_VERSION} \
    && nvm use v${NODE_VERSION} \
    && nvm alias default v${NODE_VERSION} \
    && npm --version \
    && node --version  

USER root
    
WORKDIR /app

ADD . /src/storage

RUN chown -R www-data:www-data /src/storage
```

### php.ini
``` Docker:docker/app/php.ini
zend.exception_ignore_args = off
expose_php = on
max_execution_time = 30
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /var/log/php/php-error.log
default_charset = UTF-8

[Date]
date.timezone = Asia/Tokyo

[mysqlnd]
mysqlnd.collect_memory_statistics = on

[Assertion]
zend.assertions = 1

[mbstring]
mbstring.language = Japanese
```
## DB(MariaDB)

### Dockerfile
``` Docker:docker/db/Dockerfile
FROM mariadb:latest

COPY ./docker/db/my.conf /etc/my.conf
```

### my.conf
``` docker/db/my.conf
[mysqld]
# character
character_set_server = utf8mb4
collation_server = utf8mb4_0900_ai_ci

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

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

## Web(Nginx)

### Dockerfile
``` Dockerfile:docker/web/Dockerfile
FROM nginx:1.20-alpine

ENV TZ Asia/Tokyo

COPY ./docker/web/default.conf /etc/nginx/conf.d/default.conf
```
### default.conf
``` docker/web/default.conf
server {
    listen 80;
    server_name example.com;
    root /app/public;   

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass app:9000;  
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
```

## docker-compose.yml
``` yml: docker-compose.yml
services:
  app: 
    build:
      context: .
      dockerfile: ./docker/app/Dockerfile
    user: "1000:1000"
    volumes:
      - ./src:/app
    networks:
      - Laravel_network

  web:   
    build:
      context: .
      dockerfile: ./docker/web/Dockerfile
    ports:
      - 8081:80
    depends_on:
      - app
    volumes:
      - ./src/:/app
    networks:
      - Laravel_network

  db:  
    build:
      context: .
      dockerfile: ./docker/db/Dockerfile
    ports:
      - 3306:3306
    env_file:
        - ./.env
    environment:
      MYSQL_DATABASE: ${DB_DATABASE} //.envファイルに記述
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} //.envファイルに記述
      MYSQL_PASSWORD: ${DB_PASSWORD} //.envファイルに記述
      MYSQL_USER: ${DB_USERNAME}  //.envファイルに記述 
      
      TZ: 'Asia/Tokyo'
    volumes:
      - mysql-volume:/var/lib/mysql
    networks:
      - Laravel_network
      
volumes:
  mysql-volume:

networks:
  Laravel_network:
    driver: bridge
```

## .gitignore
``` git:.gitignore
.env
```

## .env
``` .env
DB_DATABASE=データベース名
DB_USERNAME=ユーザー名
DB_ROOT_PASSWORD=rootユーザーのパスワード
DB_PASSWORD=パスワード
```

# おわりに
今回はDockerを用いてLaravel(php-fpm + mariadb + nginx)の環境を作成しました！
今回のコードはgithub上でを公開しているので，ご自身の環境に合わせて自由にカスタマイズしてください．
まだまだ勉強の身なので誤りや至らぬ点が多くあると思います．気になった点などは，コメントに残していただけると幸いです．