【Docker】Laravelの環境を作成してDockerを理解する

# はじめに

エンジニアのkeitaMaxです。

今までDockerに触れたことがなかったのですが、思い立ってDockerについて０から学習をしています。

まず最初にいつも使用しているフレームワークのLaravel の動作環境を作りたいと思っています。

PHPのコンテナ、Nginxのコンテナ、MySQLのコンテナの3つを作成します。

# 参考にする記事

https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c

この記事をもとに作成していきたいと思います。



# PHPのコンテナを起動する

まず作業用のフォルダを作成します。名前は`docker-laravel-app`にしました。

つぎに、Dockerの設定をしていきます。

`docker-laravel-app`フォルダ直下に`docker-compose.yml`ファイルを置きます。

そして`docker`フォルダを用意し、その中に`app`フォルダ、その中に`Dockerfile`ファイルと`php.ini`ファイルを置きます。

現在のフォルダ構成は以下のようになります。

```:docker-laravel-app
├ docker
│    └ app
│        ├ Dockerfile
│        └ php.ini
│
└ docker-compose.yml
```

### Dockerfile

appフォルダの中のDockerfileにはPHPの設定を記述します。

以下のように記述しました。
```dockerfile:Dockerfile
FROM php:8.3-fpm

ENV TZ Asia/Tokyo

RUN apt-get update && \
	apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \
	docker-php-ext-install intl pdo_mysql zip bcmath
		
COPY ./php.ini /usr/local/etc/php/php.ini

COPY --from=composer:2.7 /usr/bin/composer /usr/bin/composer

WORKDIR /app
```

[参考元の記事](https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c)と変わったところとわからなくて調べたところだけ書きます。
各行で何をやっているか知りたい方は[参考元の記事](https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c)をみると分かりやすく書いてあります。

```
FROM php:8.3-fpm
```

phpの最新バージョンが8.3だったので8.3-fpmを使用しています。

https://hub.docker.com/_/php


```
COPY --from=composer:2.7 /usr/bin/composer /usr/bin/composer
```

ここの`--from=composer:2.7`がわからなくて調べました。

>Optionally COPY accepts a flag --from=<name> that can be used to set the source location to a previous build stage (created with FROM .. AS <name>) that will be used instead of a build context sent by the user. In case a build stage with a specified name can't be found an image with the same name is attempted to be used instead.
(参照元:https://docs.docker.com/reference/dockerfile)

DeepLに翻訳してもらうと、
>COPYには、-from=<名前>というフラグを指定することができます。このフラグを使用すると、ユーザーから送信されたビルドコンテキストの代わりに、以前のビルドステージ（FROM ... AS <名前>で作成されたもの）が使用されます。指定した名前のビルドステージが見つからない場合は、代わりに同じ名前のイメージを使用しようとします。

おそらく、`composer:2.0`のイメージを見に行って、そこから`/usr/bin/composer`をコピーしてきてくれるってことだと思います。（間違えていたら教えていただけると助かります）

https://hub.docker.com/_/composer/tags?page=1

公式見たら、もっと新しいバージョンがあったので2.7にしました。

### php.ini

```ini: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
```

これは[参考元の記事](https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c)と同じにしています。


### docker-compose.yml

```yaml:docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: ./docker/app
      dockerfile: Dockerfile
    volumes:
      - ./src/:/app
```


こちらも[参考元の記事](https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c)とほぼ同じにしています。

```yaml:
context: ./docker/app
dockerfile: Dockerfile
```
この部分のみ変えています。

参考元のままだと以下のエラーが出てしまったので修正しました。

```console:
 => ERROR [app stage-0 3/5] COPY ./docker/app/php.ini /usr/local/etc/php/php.ini 
------
 > [app stage-0 3/5] COPY ./docker/app/php.ini /usr/local/etc/php/php.ini:
------
failed to solve: failed to compute cache key: failed to calculate checksum of ref e66b1e17-0a14-406d-9242-99b15955451a::z7zbl530wjoxlryuzh15gw5li: "/docker/app/php.ini": not found
```

`php.ini`ファイルがないといわれていたので、ここを修正し、Dockerfileも以下の部分を修正しました。

```dockerfile:Dockerfile
COPY ./php.ini /usr/local/etc/php/php.ini
```

### PHPコンテナを起動

dockerファイルができたので、以下のコマンドを実行してみます。

```console:
docker-compose up -d --build
```

色々出てくると思うのですが、以下が出てくれば立ち上がっています。

```console:
[+] Running 1/2
 - Network docker-laravel-app_default  Created
 ✔ Container docker-laravel-app-app-1  Started    
```

以下のコマンドで起動中のコンテナを見てみます。

```
PS C:\develop\docker-laravel-app> docker-compose ps
NAME                       IMAGE                    COMMAND                   SERVICE   CREATED         STATUS         PORTS
docker-laravel-app-app-1   docker-laravel-app-app   "docker-php-entrypoi…"   app       6 minutes ago   Up 6 minutes   9000/tcp
```

`docker-laravel-app-app`がいるので、無事起動できていることが確認できました。


# Nginxのコンテナを起動する


`docker`フォルダの中に`nginx`フォルダ、その中に`Dockerfile`ファイルと`default.conf`ファイルを置きます。

現在のフォルダ構成は以下のようになります。

```:docker-laravel-app
├ docker
│    ├ app
│    │   ├ Dockerfile
│    │   └ php.ini
│    │   
│    └ nginx
│        ├ Dockerfile
│        └ default.conf
│
└ docker-compose.yml
```
### Dockerfile

```Dockerfile:Dockerfile
FROM nginx:1.25-alpine

ENV TZ Asia/Tokyo

COPY ./default.conf /etc/nginx/conf.d/default.conf
```

`nginx:1.25-alpine`のバージョンがあったので1.25にしました。

https://hub.docker.com/_/nginx/tags?page=1&name=-alpine


### default.conf

```conf: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;
    }
}
```

これは[参考元の記事](https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c)と同じにしています。


### docker-compose.ymlの更新

以下のように更新しました。

```yaml:docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: ./docker/app
      dockerfile: Dockerfile
    volumes:
      - ./src/:/app
# ------以下を追記------
  web:
    build:
      context: ./docker/nginx
      dockerfile: Dockerfile
    ports:
      - 8081:80
    depends_on:
      - app
    volumes:
      - ./src/:/app
```


### Nginxコンテナを起動

以下のコマンドを実行してみます。

```console:
docker-compose up -d --build
```

コマンドが成功したら、以下のコマンドで起動中のコンテナを見てみます。

```
PS C:\develop\docker-laravel-app> docker-compose ps
NAME                         IMAGE                      COMMAND                   SERVICE   CREATED          STATUS          PORTS
docker-laravel-app-app-1     docker-laravel-app-app     "docker-php-entrypoi…"   app       38 minutes ago   Up 38 minutes   9000/tcp
docker-laravel-app-nginx-1   docker-laravel-app-nginx   "/docker-entrypoint.…"   nginx     6 seconds ago    Up 5 seconds    0.0.0.0:8081->80/tcp
```

`docker-laravel-app-nginx-1`がいるので、無事起動できていることが確認できました。

下記URLにアクセスしてみましょう。

```
http://localhost:8081
```

まだ何もファイルを置いていないので`File not found.`となっていると思います。


### 困ったことを解決した備忘録

Nginxのコンテナを立ち上げるときにエラーが特に出ていないのにpsコマンドで表示されないことがありました。

困っていましたが、Docker Desktop のほうをのログを見ると

```
2024-02-22 19:35:13 nginx: [emerg] unknown directive "//" in /etc/nginx/conf.d/default.conf:6
```
と書いてあり、エラーになっていることに気が付きました。
（`default.conf`をコピペしたせいで`//`というコメントアウトの記述が残ってしまっていました、、、）

なぜかエラーがないのにコンテナが立ち上がらないときは、Docker Desktop をみるとヒントがあるかもしれないです。


# MySQLのコンテナを起動する

`docker`フォルダの中に`db`フォルダ、その中に`Dockerfile`ファイルと`my.conf`ファイルを置きます。

現在のフォルダ構成は以下のようになります。

```:docker-laravel-app
├ docker
│    ├ app
│    │   ├ Dockerfile
│    │   └ php.ini
│    │   
│    └ nginx
│    │   ├ Dockerfile
│    │   └ default.conf
│    │   
│    └ db
│        ├ Dockerfile
│        └ my.conf
│
└ docker-compose.yml
```
### Dockerfile

```Dockerfile:Dockerfile
FROM mysql:8.3

COPY ./my.conf /etc/my.conf
```

`mysql:8.3`のバージョンがあったので8.3にしました。

https://hub.docker.com/_/mysql/tags?page=1


### my.conf

```conf: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
```

これは[参考元の記事](https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c)と同じにしています。


### docker-compose.ymlの更新

以下のように更新しました。

```yaml:docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: ./docker/app
      dockerfile: Dockerfile
    volumes:
      - ./src/:/app

  nginx:
    build:
      context: ./docker/nginx
      dockerfile: Dockerfile
    ports:
      - 8081:80
    depends_on:
      - app
    volumes:
      - ./src/:/app
# ------以下を追記------
  db:
    build:
      context: ./docker/db
      dockerfile: Dockerfile
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: database
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password
      TZ: "Asia/Tokyo"
    volumes:
      - mysql-volume:/var/lib/mysql
```
### MySQLコンテナを起動

以下のコマンドを実行してみます。

```console:
docker-compose up -d --build
```

コマンドが成功したら、以下のコマンドで起動中のコンテナを見てみます。

```
PS C:\develop\docker-laravel-app> docker-compose ps
NAME                         IMAGE                      COMMAND                   SERVICE   CREATED          STATUS          PORTS
docker-laravel-app-app-1     docker-laravel-app-app     "docker-php-entrypoi…"   app       3 hours ago      Up 3 hours      9000/tcp
docker-laravel-app-db-1      docker-laravel-app-db      "docker-entrypoint.s…"   db        28 seconds ago   Up 27 seconds   0.0.0.0:3306->3306/tcp, 33060/tcp
docker-laravel-app-nginx-1   docker-laravel-app-nginx   "/docker-entrypoint.…"   nginx     3 hours ago      Up 3 hours      0.0.0.0:8081->80/tcp
```

`docker-laravel-app-db-1`がいるので、無事起動できていることが確認できました。


# Laravelをインストールする

最後にLaravelをインストールして初期画面をブラウザで見れるようにします。

下記コマンドでappのコンテナの中に入ります。

```
docker compose exec app bash
```

`app`の部分は`docker compose ps`コマンドを打つとでてくる、`SERVICE`欄にある名前を入れます。

例えば`docker-laravel-app-db-1`に入りたい場合は以下のコマンドになります。

```
docker compose exec db bash
```

入ることができたら、以下のコマンドでLaravelを入れましょう。


```
composer create-project laravel/laravel .
```

これで最新のLaravelがインストールされます。

最後に`.`を入れることによってカレントディレクトリにそのままインストールすることができます。
これを入れないとディレクトリが新しくできてしまい、無駄にもう一階層深いところにLaravelがインストールされてしまいます。

これで先ほどの以下のURLにアクセスします。

```
http://localhost:8081/
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/6a345907-bbab-d4c2-fd71-a3365a3e1cc4.png)

Logファイルの権限が足りないというエラーが出てしまいました。

そのため、以下コマンドでstorageに権限を付けます。

```
chmod -R 775 storage
```
再度アクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/a2492918-26d6-e12e-356e-53b7b9962ac9.png)


これでLaraveLの初期画面を見ることができました。


# さいごに

Dockerはなにか難しいものだという先入観があり、なかなか挑戦できなかったのですが、いざやってみると案外簡単にコンテナを立ち上げることができました。

やり方が違ったり、もっといいやり方があるというご指摘がある方はコメントしていただけると幸いです。

最後まで読んでいただきありがとうございました。


# 参考文献

https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c

https://docs.docker.com/reference/dockerfile

# 次の記事

https://qiita.com/keitaMax/items/2a609a0e310375a2debd