DockerでLaravelの開発環境を作ってみる(5回目練習Ver)

目次

わけわからない素人がDockerでLaravelの開発環境を作る5回目です。 4回目でdocker-composeを使って複数のコンテナを作成しましたが、まだ使い物にならないため素人なりに改良してみます。

1回目はここ。

2回目はここ。

3回目はここ。

4回目はここ。

作業環境は

  • OS Ubuntu 20.04.2 LTS
  • Docker version 20.10.7
  • docker-compose version 1.29.2

です。

PHPアプリコンテナを作る

今まで通りPHPコンテナから作ります。

以下のようにディレクトリとファイルを作成します。

lwd-5
├─ docker
│   └─ php
│        │ Dockerfile  ← phpコンテナの定義ファイル
│        └
│ docker-compose.yml  ← 構成ファイル
└─ src

Composerのインストール方法

前回の"Dockerfile"内でComposerをインストールしているのですが、ハッシュ値を固定で記述していました。 このためファイルが更新されてしまったら動作しなくなります。

RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
  && php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
  && php composer-setup.php \
  && php -r "unlink('composer-setup.php');" \
  && mv composer.phar /usr/local/bin/composer

Docker17.05以降に使えるようになったマルチステージビルドという機能で、直接composerのDockerイメージから持ってくることにします。 2021年7月8日現在では2.1.3が最新でしたのでこれを使用します。

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

マルチステージビルドの方法は上記のページにあるので、それにタグ「2.1.3」を指定します。

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

Dockerfile

# FROM : これから作るイメージの元ととなるイメージ
# Debian 10でphp8.0.7のFastCGI実装イメージを元に作成する
FROM php:8.0.7-fpm-buster

# マルチステージビルドでComposerをインストールする
COPY --from=composer:2.1.3 /usr/bin/composer /usr/bin/composer

# Laravelのインストール
RUN apt-get update \
  && apt-get install -y \
  git \
  unzip \
  && composer global require laravel/installer \
  && docker-php-ext-install pdo_mysql

docker-compose.yml

version: "3.8"                    # Docker Engin 19.03.0以降

services:
    # PHP サービス
    php:
        build: ./docker/php       # Dockerファイルの場所を指定
        volumes:
            - ./src/:/var/www     # Laravelアプリの配置ディレクトリ
        networks:
            - examnet

# Dockerネットワーク
networks:
    # コンテナ同士はホストから独立したネットワークで通信し、"appnet"という名前をつける
    appnet:
        driver: bridge

Composerが使えるか確認するためコンテナを実行します。

$ cd lwd-5
~/lwd-5$ docker-compose up -d

docker-compose psでコンテナが実行されているか確認することができます。

~/lwd-5$ docker-compose ps
   Name                  Command              State    Ports  
--------------------------------------------------------------
lwd-5_php_1   docker-php-entrypoint php-fpm   Up      9000/tcp

phpコンテナが起動していることが確認できたので、中に入ってComposerのバージョンを確認してみます。

~/lwd-5$ docker-compose exec php /bin/bash
root@92b9e4539390:/var/www/html# composer --version
Composer version 2.1.3 2021-06-09 16:31:20

バージョン2.1.3と表示されました。

Composerのインストールまではできたのでexitコマンドでコンテナから脱出します。

apt-getのキャッシュを削除

Dockerの「Dockerfileを書くためのベスト​プラクティス」ページによると、aptキャッシュのクリアがイメージサイズの減少になるそうです。

まず作成したコンテナのファイルサイズを確認してみます。

~/lwd-5$ docker-compose images
 Container    Repository    Tag       Image Id       Size  
-----------------------------------------------------------
lwd-5_php_1   lwd-5_php    latest   38fc1932dbd8   466.7 MB

DebianとUbuntuの公式イメージでは自動的にapt-get cleanが実行されるようですが、PHPイメージなので一応下記のように追加しました。

&& apt-get clean && rm -rf /var/lib/apt/lists/*

修正したDockerfileは以下になります。

# FROM : これから作るイメージの元ととなるイメージ
# Debian 10でphp8.0.7のFastCGI実装イメージを元に作成する
FROM php:8.0.7-fpm-buster

# マルチステージビルドでComposerをインストールする
COPY --from=composer:2.1.3 /usr/bin/composer /usr/bin/composer

# Laravelのインストール
RUN apt-get update \
  && apt-get install -y \
  unzip \
  git \
  && composer global require laravel/installer \
  && docker-php-ext-install pdo_mysql \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

今のコンテナを削除し、再作成してみます。

~/lwd-5$ docker-compose down
~/lwd-5$ docker image rm lwd-5_php
~/lwd-5$ docker-compose up -d

コンテナが作成されたのでサイズを確認して見ます。

~/lwd-5$ docker-compose images
 Container    Repository    Tag       Image Id       Size  
-----------------------------------------------------------
lwd-5_php_1   lwd-5_php    latest   10b311c5b775   449.1 MB

466.7 MBから449.1 MBに17.6 MB小さくなりました。

UNIXドメインソケットを使用する

前回まではphpとnginxのコンテナ間通信にTCPを使用していました。 ですが、UNIXドメインソケットの方が効率が良いと言うので変更します。

下記のようにディレクトリと設定ファイルを作ります。

lwd-5
├─ docker
│   ├─ nginx  ← 作成するディレクトリ
│   │   │ default.conf  ← nginxの設定ファイル
│   │   └
│   └─ php
│        │ Dockerfile  ← phpコンテナの定義ファイル
│        └ php-fpm.d   ← 作成するディレクトリ
│           │ zzz-www.conf  ← Unixドメインソケットの設定ファイル
│           └
│ docker-compose.yml  ← 構成ファイル
└─ src

PHPコンテナで使用するUnixドメインソケットの設定ファイルを作成します。

https://www.php.net/manual/en/install.fpm.configuration.php

設定ファイル名を"zzz-www.conf"にしているのは、公式イメージにある設定を上書きするためです。 公式イメージの内容をdocker historyコマンドで確認すると。

$ docker history --no-trunc php:8.0.7-fpm-buster
・・・中略・・・
<missing>                                                                 5 weeks ago    /bin/sh -c set -eux;  cd /usr/local/etc;  if [ -d php-fpm.d ]; then   sed 's!=NONE/!=!g' php-fpm.conf.default | tee php-fpm.conf > /dev/null;   cp php-fpm.d/www.conf.default php-fpm.d/www.conf;  else   mkdir php-fpm.d;   cp php-fpm.conf.default php-fpm.d/www.conf;   {    echo '[global]';    echo 'include=etc/php-fpm.d/*.conf';   } | tee php-fpm.conf;  fi;  {   echo '[global]';   echo 'error_log = /proc/self/fd/2';   echo; echo '; https://github.com/docker-library/php/pull/725#issuecomment-443540114'; echo 'log_limit = 8192';   echo;   echo '[www]';   echo '; if we send this to /proc/self/fd/1, it never appears';   echo 'access.log = /proc/self/fd/2';   echo;   echo 'clear_env = no';   echo;   echo '; Ensure worker stdout and stderr are sent to the main error log.';   echo 'catch_workers_output = yes';   echo 'decorate_workers_output = no';  } | tee php-fpm.d/docker.conf;  {   echo '[global]';   echo 'daemonize = no';   echo;   echo '[www]';   echo 'listen = 9000';  } | tee php-fpm.d/zz-docker.conf
・・・省略・・・

“zz-docker.conf"ファイルで’listen = 9000’と設定されています。

zzz-www.conf

[www]
; FastCGI リクエストを受け入れるアドレス
listen = /var/run/php-fpm/php-fpm.sock
; unix ソケットを使う場合に、そのパーミッションを設定します。
; Webサーバーからの接続ができるように設定します。
listen.owner = www-data
listen.group = www-data
listen.mode = 0666
; プロセスマネージャが子プロセスの数を制御する方法を選択します。
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

PHPのDockerfileに設定ファイルのコピー処理を追加します。

# FROM : これから作るイメージの元ととなるイメージ
# Debian 10でphp8.0.7のFastCGI実装イメージを元に作成する
FROM php:8.0.7-fpm-buster

# マルチステージビルドでComposerをインストールする
COPY --from=composer:2.1.3 /usr/bin/composer /usr/bin/composer

# Laravelのインストール
RUN apt-get update \
  && apt-get install -y \
  unzip \
  git \
  && composer global require laravel/installer \
  && docker-php-ext-install pdo_mysql \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

# Unixドメインソケットを使うための設定ファイルをコピー
COPY ./php-fpm.d/zzz-www.conf /usr/local/etc/php-fpm.d/zzz-www.conf

nginxのPHPサーバーとの通信方法を変更します。

“default.conf"ファイル。

server {
    listen 80;
    server_name example.com;
    root /var/www/example-app/public;

    add_header X-Frame-Options "SAMEORIGIN";
    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$ {
        # PHP FastCGI サーバーとUnixドメインソケットで通信する
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

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

“docker-compose.yml"ファイルにnginxコンテナを追記します。

version: "3.8"                 # Docker Engin 19.03.0以降

services:
    # PHP サービス名:php
    php:
        build: ./docker/php    # Dockerファイルの場所を指定
        volumes:
            # Laravelアプリの配置ディレクトリ
            - ./src/:/var/www
            # unixドメインソケット通信で使用
            - php-fpm-socket:/var/run/php-fpm
        networks:
            - appnet

    # nginx Webサーバー サービス名:web
    web:
        # 作成元イメージ
        image: nginx:1.21.0-alpine
        # nginxコンテナのポート80を外部にポート8080として公開する
        ports:
            - 8080:80
        # nginxコンテナはPHPのコンテナに依存 先にphpコンテナを起動する
        depends_on:
            - php
        volumes:
            # アプリディレクトリ
            - ./src:/var/www
            # nginxの設定ファイルを置き換える
            - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
            # unixドメインソケット通信で使用
            - php-fpm-socket:/var/run/php-fpm
        networks:
            - appnet
    
# Dockerネットワーク
networks:
    # コンテナ同士はホストから独立したネットワークで通信し、"appnet"という名前をつける
    appnet:
        driver: bridge

volumes:
    # phpとnginxがunixドメインソケットで通信するために使用
    php-fpm-socket:

定義ファイルを作成したら前に作ったイメージを削除し、コンテナを再実行します。

~/lwd-5$ docker image rm lwd-5_php
~/lwd-5$ docker-compose up -d

phpとnginxのコンテナが実行中になります。

~/lwd-5$ docker-compose ps
   Name                  Command               State                  Ports                
-------------------------------------------------------------------------------------------
lwd-5_php_1   docker-php-entrypoint php-fpm    Up      9000/tcp                            
lwd-5_web_1   /docker-entrypoint.sh ngin ...   Up      0.0.0.0:8080->80/tcp,:::8080->80/tcp

phpコンテナに入りLaravelアプリを新規作成します。

~/lwd-5$ docker-compose exec php /bin/bash
root@92b9e4539390:/var/www/html# export PATH="$HOME/.composer/vendor/bin:$PATH"
root@92b9e4539390:/var/www/html# cd ..
root@92b9e4539390:/var/www# laravel new example-app
root@c6c4226fd32e:/var/www# chmod o+w example-app/storage/logs
root@c6c4226fd32e:/var/www# chmod o+w example-app/storage/framework/sessions
root@c6c4226fd32e:/var/www# chmod o+w example-app/storage/framework/views

次にブラウザで"http://127.0.0.1:8080/“を開けばいつもの画面が表示されます。

MySQLコンテナを作る

MySQLのデータ置き場を前回はホストのディレクトリに割り当てていましたが、これをDocker Enginが管理するボリュームに変更します。

lwd-5
├─ docker
│   ├─ nginx
│   │   │ default.conf
│   │   └
│   └─ php
│        │ Dockerfile
│        └ php-fpm.d
│           │ zzz-www.conf
│           └
│ docker-compose.yml  ← MySqlコンテナを追加する
└─ src
     └─ example-app
          │ .env  ← MySQLへの接続設定する
          └─

元からある.envファイルにMySqlへの接続情報を設定します。

~/lwd-5/src/example-app/.envファイルの下記の箇所を修正します。

・・・省略・・・
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laraveldb
DB_USERNAME=user4th
DB_PASSWORD=pwuser4th
・・・省略・・・

“docker-compose.yml"ファイルにMySQLコンテナの定義を追記します。 PHPコンテナの接続設定で使用した値を環境変数に設定します。

version: "3.8"                 # Docker Engin 19.03.0以降

services:
    # PHP サービス名:php
    php:
        build: ./docker/php    # Dockerファイルの場所を指定
        volumes:
            # Laravelアプリの配置ディレクトリ
            - ./src/:/var/www
            # unixドメインソケット通信で使用
            - php-fpm-socket:/var/run/php-fpm
        networks:
            - appnet

    # nginx Webサーバー サービス名:web
    web:
        # 作成元イメージ
        image: nginx:1.21.0-alpine
        # nginxコンテナのポート80を外部にポート8080として公開する
        ports:
            - 8080:80
        # nginxコンテナはPHPのコンテナに依存 先にphpコンテナを起動する
        depends_on:
            - php
        volumes:
            # アプリディレクトリ
            - ./src:/var/www
            # nginxの設定ファイルを置き換える
            - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
            # unixドメインソケット通信で使用
            - php-fpm-socket:/var/run/php-fpm
        networks:
            - appnet
    # MySQL データベースコンテナ サービス名:mysql
    mysql:
        image: mysql/mysql-server:8.0.25
        # MySQLコンテナで使用する環境変数
        # コンテナ作成時にこの内容で初期化さされます
        # laraveldbデータベース、user5thユーザーがpwuser5thパスワードで作成され
        # rootユーザーバスワードはmy-root-pwになります
        environment:
            MYSQL_DATABASE: laraveldb
            MYSQL_USER: user5th
            MYSQL_PASSWORD: pwuser5th
            MYSQL_ROOT_PASSWORD: my-root-pw
        # ホストのディレクトリをコンテナに割り当てる
        # そうしないとコンテナを終了したらデータが残らない
        volumes:
            - .mysql-data:/var/lib/mysql
        networks:
            - appnet
    
# Dockerネットワーク
networks:
    # コンテナ同士はホストから独立したネットワークで通信し、"appnet"という名前をつける
    appnet:
        driver: bridge

volumes:
    # phpとnginxがunixドメインソケットで通信するために使用
    php-fpm-socket:
    # MySqlデータの保管場所
    mysql-data:

今まで作成したコンテナはいらないのでdocker-compose downコマンドで削除し、docker-compose up -dでまとめて再作成します。

~/lwd-5$ docker-compose down
~/lwd-5$ docker-compose up -d

php、nginx、mysqlの3つのコンテナが作成されました。

~/lwd-5$ docker-compose ps
    Name                   Command                  State                      Ports                
----------------------------------------------------------------------------------------------------
lwd-5_mysql_1   /entrypoint.sh mysqld            Up (healthy)   3306/tcp, 33060/tcp, 33061/tcp      
lwd-5_php_1     docker-php-entrypoint php-fpm    Up             9000/tcp                            
lwd-5_web_1     /docker-entrypoint.sh ngin ...   Up             0.0.0.0:8080->80/tcp,:::8080->80/tcp

phpコンテナに入り、アプリのディレクトリへ移動して、migrateコマンドを実行します。

~/lwd-5$ docker-compose exec php /bin/bash
root@a67c48298462:/var/www/html# cd /var/www/example-app
root@a67c48298462:/var/www/example-app# php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (696.94ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (542.47ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (535.14ms)

正常に終わったようなので、mysqlコンテナを確認してみます。

~/lwd-5$ docker-compose exec mysql /bin/bash
bash-4.4# mysql -u user5th -p
Enter password: 
mysql> use laraveldb;
mysql> show tables;
+---------------------+
| Tables_in_laraveldb |
+---------------------+
| failed_jobs         |  ← phpコンテナからテーブルが作成されています
| migrations          |
| password_resets     |
| users               |
+---------------------+
4 rows in set (0.00 sec)

phpからMySQLへの接続ができていることを確認できました。