【docker compose】PHPとMySQLの開発環境を作ってみた

# docker composeでPHPとMySQLの環境を

今、2025/05 時点の記事です。

Laravelの検証環境を作ろうとして、チャッピー(＝ChatGPT)に聞きながら検証して作った作業手順および解説となります。例によって個人的の備忘録です。この手の作業に慣れていない人向けで、慣れた人には今さらなレベルですが、参考に出来る人はしてもらえればなーと。

今回導入するのはLaravel 12、 PHP 8.2、 MySQL 8.4 です。
なお、バージョン番号は融通が利きます。

OSは、クリーンインストールした Ubuntsu 24.04 を使って進めていきます。
昔、[WSLを使ってWindows上にLinuxを動かす](https://qiita.com/Tabito/items/14503998d6f3a46eb6f6 "【WSL】Windows上でLinuxを動かす今時の方法")という記事を書いたんですが、あれの続きです。なので、WSLの記事として書いていますが、WSLである必要はないと思います。

:::note warn
* Ubuntsu 24.04 で検証しています
:::
## Docker Composeについて
Docker Compose とは、複数コンテナのアプリケーションを定義・共有するために役立つツール‥‥ということで、ようはPHPサーバーとMySQLのサーバー、２つの仮想サーバーをセット運用することでコマンド一つでシステムの開発環境を動作できるぞという代物です。

最初に共有しておくべき知識が２つあります。
* **docker-composeとdocker composeは別のもの**
docker-composeは`docker-compose`というコマンドで、docker composeは`docker`コマンド内の１機能です。結論から言うとdocker composeの方が新しいので、必要なのはdockerだけです。本記事もこちらを取り扱います。
* **dockerのコンテナのOSは、dockerを起動しているOS（ホストOS）とは異なる**
例えば、本記事では、WSLの上のUbuntsuでphp:7.4-apacheというコンテナを動かすのですが、このコンテナのOSはDebian系になるそうです。なのでUbuntsuの上で直にシステムを動かすのとは厳密には挙動が異なります。OSの機能をフル活用するタイプのプログラムとは相性が良くない事には注意が必要です。

## Dockerの導入
とりあえず、Docker自体を動作させないことには始まりませんので入れていきます。

### インストール

まずは、Dockerをインストールです。

```bash
sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg lsb-release

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
"deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

sudo usermod -aG docker $USER
```
```bash
docker -v
Docker version 28.1.1, build 4eba377
```
``docker -v ``を実行して、バージョンが出てくれば完了。
ややこしいですが、一回やればOKなんで呪文として唱えましょう。

<details><summary>もちろん詠唱に意味はあります</summary>

下から解説します。

最後から２行目の``apt install``がdockerのインストールコマンド本体です。

aptコマンドは自動的に``/etc/apt/sources.list.d/``の下にあるlistファイルを参照してその内容を元にソフトを導入します。なので、そこに``docker.list``という名前でインストールに必要な情報を配置します。
echo文で出力しているのがその内容で、その中にはOSの情報が含まれています。

また、Dockerをインストールするときには、gpgファイルが必要です。これは、dockerが改竄されていないかどうかをチェックするためのファイルであり、curlコマンドでdocker公式から取得します。
gpgコマンドはテキストファイルをバイナリに変換するためのもので、変換したファイルを``/etc/apt/keyrings``に配置します。（配置場所自体はどこでも良いものと思います）

最後のusermodコマンドは、dockerをsudoでなくても起動するよう権限を変更するためのものとのこと。(このコマンドは、反映には再起動が必要になります)
‥ですが、結局私のは上手くいかなかったようで結局この後もsudoしてます。
</details>

### Deamonの起動

systemctlで確認・起動・終了ができます。起動していなければ起動しましょう。
```bash
# ステータス確認
sudo systemctl status docker
# 起動
sudo systemctl start docker
# 終了
sudo systemctl stop docker
```
systemctlが使えない環境ならserviceコマンド(``sudo service docker start``等)です。
（あまり良くわからないので、この環境ではこっちなんだなーくらいで良いと思いますが）

## MySQLとPHP動作環境の構築
Dockerが動作したので、いよいよコンテナを作りましょう。
まずは考えることの少ないDBサーバーを作ってdocker composeが動作することを確認、それからアプリサーバーとします。

### 1) プロジェクトフォルダを作成する
適当な名前を付けます。以後はこのフォルダ内で作業します。
その下にDocker関連ファイルを入れるためのフォルダとlaravelのソースを置くフォルダを作っておきます。
```bash
mkdir test-pj
cd test-pj

mkdir docker
mkdir src
```

### 2) docker-compose.ymlを作成する

docker-compose.ymlという名前のファイルを作ります。これは固定ファイル名です。
viなりお好きなテキストエディタで。
```bash
touch docker-compose.yml
vi docker-compose.yml
```
```yml
services:
  db:
    image: mysql:8.4
    container_name: mysql84__tpj
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: pass001
      MYSQL_DATABASE: laradb
      MYSQL_USER: laravel
      MYSQL_PASSWORD: larapass
    ports:
      - "3306:3306"
    volumes:
      - ./docker/db_data:/var/lib/mysql
    networks:
      - laravel-net

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: pma__tpj
    restart: always
    ports:
      - "8888:80"
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
      UPLOAD_LIMIT: 100M
    depends_on:
      - db
    networks:
      - laravel-net

networks:
  laravel-net:
```

サービスを２つ登録しています。（ここでは、サービス・コンテナ・サーバーほぼ同じものです）
ひとつは、``db``サービスで、これを``mysql:8.4``というイメージをベースに生成します。この8.4の部分がMySQLのバージョンなので、例えば``mysql:5.7``とやれば5.7が導入できます。
もうひとつは、それを確認するためのphpMyAdminを動かす``phpmyadmin``サービスです。これも専用のイメージが用意されています。
それぞれcontainer_nameを名付けます。test pjなので``__tpj``等をくっつけてみました。必須ではないです。

<details><summary>より詳細な解説はこちら</summary>

**[dbサービス]** 

environment部分、MYSQL_ROOT_PASSWORDがrootユーザのパスワードとなります。
加えて、１つのDBスキーマ、および、それにアクセスできるユーザを1人登録します。
この例の場合ですと、
``root`` / `` pass001`` と、 ``laravel`` / `` larapass`` の２名でログインが可能、そして後者は、空のスキーマである `laradb`にのみ、アクセスできます。

**[pmaサービス]**

ports部分は外(ホストOS)のポートを中(コンテナOS)のポートで書き換えるものになります。
この例では、ローカルマシンの8888ポートに接続することでコンテナ内の80で起動しているhttpサーバへつなぐことを意味します。
environmentでUPLOAD_LIMITを指定しましょう。デフォルトだとアップロード上限が2MBになります。
depend_onは、依存関係、主に起動順序を制御するためのものです。この書き方で、dbサービス → pmaサービスという順で起動するようになります。

**その他**

networksは文字通り、ネットワークを作ります。 統一されていれば名前は任意です

</details>

<details><summary>Volumeの記載について</summary>

上記の例では、こう設定しました。
```yml
    volumes:
      - ./docker/db_data:/var/lib/mysql
```
意味は、プロジェクトフォルダのdocker/db_dataフォルダをコンテナ内OSでの/var/lib/mysqlフォルダとして扱う、です。このように書くと、構築時にdb_dataフォルダが生成され、そこにMySQLの諸々ファイルが配置されます。

一方、こういう書き方もあります。
```yml
    volumes:
      - db_data:/var/lib/mysql
```
とした上で末尾に
```yml
volumes:
  db_data:
```
と書く書き方です。

この書き方だとdocker側がボリュームを管理してくれるため、プロジェクトフォルダ内はすっきりします。
一方で、作業が終わってプロジェクトフォルダを削除してもデータが残り続け、別途消す必要が出てきますので、どちらが良いかはケースバイケースですね。
いちおう、出来たボリュームは``docker volume ls``などで確認することができます。

</details>

### 3) Dockerコンテナをビルドしてから実行

ymlが正しければ、ビルド実行は１コマンドです。
```bash
   docker compose up -d --build
```
``-d``はバックグラウンド実行を表し、``--build``がビルド実行を意味します。

### 4) phpMyAdminで接続確認

http://127.0.0.1:8888/
に接続を試みます。

phpMyAdminの画面が表示され、``root`` / `` pass001``でログインできれば、docker composeの導入には成功しています。

### 5) Dockerコンテナの終了

```bash
   docker compose down
```
downで終了します。

### 6) Dockerfileを作成する

mySQLやphpMyAdminくらいであればymlの設定で十分に動くのですが、アプリサーバはそうはいかず、ビルド時のセットアップ時にいろいろ指示をする必要があります。そこで必要になるのがDockerfileです。

（ちなみに、このファイル名はこのあとymlで指定する為、何でもよいです）

```bash
touch docker/Dockerfile
vi docker/Dockerfile
```
```bash
FROM php:8.2-apache

# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
    libzip-dev zip unzip git curl libonig-dev \
    && docker-php-ext-install pdo_mysql mbstring zip

# Composerのインストール
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer

# Apacheのmod_rewriteを有効化
RUN a2enmod rewrite

# ドキュメントルート指定
RUN sed -i 's|DocumentRoot /var/www/html|DocumentRoot /var/www/html/public|g' /etc/apache2/sites-available/000-default.conf

# ワークフォルダの変更
WORKDIR /var/www/html
```
冒頭の``FROM php:8.2-apache``が、PHP8.2とapacheのセットを表しています。
``php:7.4-apache``のようなバージョン違いのほか、Nginxなどを使える指定もあります。

パッケージのインストール方法はOSに依存するわけですが、ここではコンテナ側のDebian系OSの書き方になります。ubuntuとはちょっと異なる感じになるなので、追加で必要なパッケージがあった場合は、ChatGPTにでも聞いてね。

<details><summary>ドキュメントルートについて</summary>

デフォルトの設定だとドキュメントルートは `/var/www/html`こと `src` です。しかしLaravelのドキュメントルートは `/var/www/html/public` こと ` src/public` です。
そこで、apacheのconfに対して文字列置換をしています。Laravelを動かすだけならこれで良いのですが、もっと複雑なconfを書きたい場合は、別途confファイルを作ってそれと差し替えるという手もあります。
</details>

### 7) docker-compose.ymlの追記

docker-compose.ymlに`app`サービスを追加します。書くのは``services:``と``db``の間です。
```yml
# services:
  app:
    build:
      context: .
      dockerfile: docker/Dockerfile
    container_name: laravel12__tpj
    volumes:
      - ./src:/var/www/html
    ports:
      - "8080:80"
    depends_on:
      - db
    working_dir: /var/www/html
    networks:
      - laravel-net
#  db:
#    image: mysql:8.4
#  ：
```
先ほど作ったDockerfileを指定しておきます。
`ports`は`8080:80`なので、ローカルで8080ポートに接続すると、コンテナ内の80ポートに繋がる設定です。

### 8) Dockerコンテナ再ビルド実行

ビルド実行します。
```bash
docker compose up -d --build
```

### 9) laravel導入
#### 9-1) laravel 新規PJ導入

空のlaravel 12をインストールする場合：
composerコマンドを使ってLaravelを入れるのですが、このコマンドはコンテナ内で実行する必要があります。コンテナ内でコマンドを打ちたい場合`` docker compose exec [サービス名] [コマンド]``を使います。
つまり、インストールコマンドはこうなります。
```bash
  docker compose exec app composer create-project laravel/laravel:^12.0 .
```
laravelフォルダ内にソースが出来たことを確認します。

#### 9-2) laravel 既存PJ導入

すでに存在するlaravelを入れる場合：
git cloneなり手動配置なりで、コード一式をsrcフォルダに配置します。

続いて、composerで依存するパッケージのインストールとアプリキーの生成を行います。
この辺は通常通りといえばそうなのですが、各コマンドはコンテナ内で実行する必要があり、`` docker compose exec [サービス名] [コマンド]``の構文で実行する必要があります。
```bash
docker compose exec app composer update
docker compose exec app composer install
docker compose exec app php artisan key:generate
```

### 10) .env書換え

src/.envを書き換えます。
Laravel 12はデフォルトSQLiteなので、mysqlに書き換えます。
コメントアウトを解除しつつ進めましょう。
DBのhostはDockerサービス名である``db``を指定します。DB名・パスはymlで指定した通り。
```
：
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laradb
DB_USERNAME=root
DB_PASSWORD=pass001
：
```

### 11) 権限付与

Laravelでシステムが操作するフォルダのパーミッションを変更。
```bash
docker compose exec app chmod -R 777 storage bootstrap/cache
```

### 12) DBマイグレーション

Laravel 12の新規導入するとインストール時にマイグレーションが掛かりますが
あれはsqliteが対象となっていましたので、MySQLに切り替えた後、改めて実行する必要があります。
```bash
docker compose exec app php artisan migrate:fresh -v --seed
```

### 13) 接続確認完了

http://127.0.0.1:8080/
に接続を試みます。
laravelのトップ画面が出てくれば完了です。

お疲れ様でした。

## Dockerコンテナの操作

```bash
# 起動
docker compose up -d
# 終了
docker compose down
# 確認
docker compose ps
```
``-d``はバックグラウンド実行を意味します。
execでコンテナ内にコマンドが発行できるのは前述しましたが、bashを実行するとコンテナ内に入って作業できます。
```bash
docker compose exec app bash
```

## 最終的な構成：

プロジェクトフォルダ
```
test-pj
 ┣ docker-compose.yml　  docker compose用のyml
 ┣ docker ┳ Dockerfile　 アプリサーバーの設定ファイル
 ┃        ┗ db_data      MySQLの動作ファイル
 ┗ src                   Laravelのルートフォルダ
```

<details><summary>docker-compose.yml</summary>

```yml
services:
  app:
    build:
      context: .
      dockerfile: docker/Dockerfile
    container_name: laravel12__tpj
    volumes:
      - ./src:/var/www/html
    ports:
      - "8080:80"
    depends_on:
      - db
    working_dir: /var/www/html
    networks:
      - laravel-net
  db:
    image: mysql:8.4
    container_name: mysql84__tpj
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: pass001
      MYSQL_DATABASE: laradb
      MYSQL_USER: laravel
      MYSQL_PASSWORD: larapass
    ports:
      - "3306:3306"
    volumes:
      - ./docker/db_data:/var/lib/mysql
    networks:
      - laravel-net

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: pma__tpj
    restart: always
    ports:
      - "8888:80"
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
      UPLOAD_LIMIT: 100M
    depends_on:
      - db
    networks:
      - laravel-net

networks:
  laravel-net:
```
</details>
<details><summary>docker/Dockerfile</summary>

```yml
FROM php:8.2-apache

# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
    libzip-dev zip unzip git curl libonig-dev \
    && docker-php-ext-install pdo_mysql mbstring zip

# Composerのインストール
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer

# Apacheのmod_rewriteを有効化
RUN a2enmod rewrite

# ドキュメントルート指定
RUN sed -i 's|DocumentRoot /var/www/html|DocumentRoot /var/www/html/public|g' /etc/apache2/sites-available/000-default.conf

# ワークフォルダの変更
WORKDIR /var/www/html
```
</details>

## まとめ

今回はdocker composeで、PHP 8.2、MySQL 8.4 の環境を作成し、Laravel 12をセットアップしました。

この手順を少し修正すれば、PHP・MySQL・Laravelいずれも好きなバージョンを選ぶことができますし、Laravelに限らず、PHPとMySQLで動作するものは大抵動かせると思います。環境が出来てしまえば、upとdownコマンドで起動を切り替えられるので相当便利と思います。試してみてください。

----
独学のため正確でない可能性があります。
（っ・ｘ・）っ きゅ