Docker for Mac で PHP8系, nginx, mysql のシンプルな開発環境構築

弊社では通常、ローカルの開発環境に Docker を利用しております。開発環境の構築にあたりいくつか記事を参考したのですが、自分にとって不要なことが多く、なるべくシンプルな雛形を作成したので以下に記述します。Docker 内に web、アプリケーション、データベースの3つのみ配置したシンプルな構成です。

Docker for Mac のインストール

下記より Docker for Mac をインストールします。インストールが完了したら、基本の準備はこれで完了です。あとはインストールした Docker 環境内に web アプリ開発に必要な環境を構築していきます。

Docker のマニュアル、インストールページ

コンテナ&ディレクトリの構成検討

ファイルサイズをなるべく削減したいとか常に最新を使いたいとか、特にこだわりがなかったのと、他の人が見て直感的にわかりやすいのがいいと思い、現時点で標準的なものを選びました。また、細かいバージョンまで固定することで各自の環境をブレないように意識しました。加えて、git を使用して本番環境へソースコードを簡単に反映したかったので、自分のPC内でのディレクトリ構成も少し慎重に考えました。

# 実際に動作確認ができている構成とバージョン
web: nginx:1.15.2
app: php-fpm:8.1.8
db: mysql:5.7.23

# ディレクトリ構成
# ここの下にCakePHPを展開
htdocs/projectName/app
# ここの下に Docker 周りのファイルを展開
htdocs/projectName/docker

ファイル構成一式

これから色々記述していきますが、先にファイル一覧を見た方が全体を把握しやすいかもしれませんので、こちらにファイルリストを記載しておきます。

htdocs/projectName/app # ソースコード用ディレクトリ
htdocs/projectName/app/webroot/index.php

htdocs/projectName/docker # Docker 用ディレクトリ
htdocs/projectName/docker/docker-compose.yml
htdocs/projectName/docker/Dockerfile
htdocs/projectName/docker/get_composer.sh
htdocs/projectName/docker/nginx.conf
htdocs/projectName/docker/php.ini

docker-compose.yml の記述

Docker の構成を管理するファイル、docker-compose.yml を下記を参考に記述していきます。なお、ファイルは上記ディレクトリ構成の docker ディレクトリ内に 保存しました。とりあえず完成した内容を先にご紹介し、各設定の解説は後述します。インデントは半角スペース 2 つで記載しており、適当なインデントで書くと思わぬエラーが発生するので慎重に記述します。

https://docs.docker.jp/compose/compose-file/

# 以下の場所にファイルを作成&下記内容を記述
htdocs/projectName/docker/docker-compose.yml

docker-compose.yml の内容

services:
  web:
    container_name: web
    image: nginx:1.15.2
    ports:
      - "80:80"
    depends_on:
      - app
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ../app:/var/www/html

  app:
    container_name: app-my-project
    build: ./
    depends_on:
      - db
    volumes:
      - ./php.ini:/usr/local/etc/php/php.ini
      - ../app:/var/www/html

  db:
    container_name: db
    image: mysql:5.7.23
    environment:
      - MYSQL_DATABASE=my_app
      - MYSQL_USER=my_app
      - MYSQL_PASSWORD=secret
      - MYSQL_RANDOM_ROOT_PASSWORD=yes
      - TZ=Japan
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    ports:
      - "3306:3306"
    volumes:
      - db-my-project:/var/lib/mysql

volumes:
  db-my-project:

web コンテナ(nginx)の設定

ブラウザで http://localhost とアクセスした時に最初に到達するのが web コンテナになります。こちらにきたアクセスを次項で説明する app コンテナに連携していきます。コンテナ名を分かりやすく web とし、どの image を使用してコンテナを作成するのか指定しました。80 番ポートでアクセスされたものをそのまま 80 番で転送し、web コンテナは app コンテナに依存するため依存コンテナを指定します。最後に、自分のPCの内容をコンテナ内の内容と同期(上書き)するため、それぞれ同期したいパスを記述します。

web:
  container_name: web # コンテナ名
  image: nginx:1.15.2 # 公式 image の指定
  ports:
    - "80:80"
  depends_on: # 依存関係のコンテナ、container_name で指定した名前ではない
    - app
  volumes:
    # 自分のPC内、同一階層にある nginx.conf ファイルをコンテナ内の指定ディレクトリに同期(上書き)する
    - ./nginx.conf:/etc/nginx/conf.d/default.conf
    # 自分のPC内、一階層上にある app ディレクトリをコンテナ内のhtmlディレクトリと同期させる
    - ../app:/var/www/html

nginx.conf ファイルの作成と内容の記述

上記で nginx.conf という自分のPC内のファイルをコンテナ内と同期する設定を記載したため、docker ディレクトリ内に nginx.conf ファイルを作成し、下記の内容を記述します。こちらは CakePHP ベースとなっていますが、WordPress などの開発環境を作る際も概念は一緒です。

# 以下の場所にファイルを作成&下記内容を記述
htdocs/projectName/docker/nginx.conf
# nginx.confの内容、cakephpの場合
server {
    listen 80;
    server_name _;

    proxy_no_cache      1;
    proxy_cache_bypass  1;
    sendfile            off;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    # コンテナ内におけるドキュメントルート
    root  /var/www/html/webroot;
    index index.php;

    access_log /var/log/nginx/access.log;
    error_log  /var/log/nginx/error.log;

    client_max_body_size 20M;

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

    location ~ \.php$ {
        try_files   $uri =404;
        include fastcgi_params;
        # こちらにアプリケーションコンテナを指定
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_intercept_errors on;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

app コンテナ(php-fpm)の設定

ブラウザで http://localhost とアクセスすると、上記 web コンテナを経由してこれから設定する app コンテナへリクエストが到達します。app コンテナがリクエストを受け取ると php のプログラムが実行され、必要に応じて DB 接続等を行い、実行結果を web コンテナを経由してブラウザへ返却していきます。app コンテナは php-fpm ベースで作成したいのですが、追加モジュール等をインストールしたい & composer を使えるようにしておきたいので、image ファイル名を指定せず、Dockerfile にて諸々記述していきます。また、先ほどと同様、自分の PC の内容をコンテナの内容と同期しておきます。

app:
  container_name: app-my-project # コンテナ名
  # ファイル名を省略すると Dockerfile ファイルが参照され、そのファイル内で image を指定
  build: ./ 
  depends_on: # 依存関係のコンテナ、container_name で指定した名前ではない
    - db
  volumes:
    # 自分のPC内、同一階層にある php.ini ファイルをコンテナ内の指定ディレクトリに同期(上書き)する
    - ./php.ini:/usr/local/etc/php/php.ini
    # 自分のPC内、一階層上にある app ディレクトリをコンテナ内のhtmlディレクトリと同期させる
    - ../app:/var/www/html

Dockerfile ファイル、get_composer.sh ファイルの作成と内容の記述

Dockerfile に記載する内容で、app コンテナを立ち上げる際に使用する image を新しく作成します。なお、image の作成時にインストールしている php モジュールは wordpress で必要だったものも含まれており、弊社の開発環境の基本となっております。同一階層に composer をインストールするためのコマンドファイル(get_composer.sh)を配置し、それを image ファイル作成時に実行しております。

# 以下の場所にファイルを作成&下記内容を記述
htdocs/projectName/docker/Dockerfile
htdocs/projectName/docker/get_composer.sh
# Dockerfile の内容
FROM php:8.1.8-fpm

# バックスラッシュでコマンドを結合し、容量を圧縮する
RUN set -x \
    && apt-get update \
    && apt-get install -y libicu-dev libpng-dev libzip-dev wget zip unzip vim \
    && docker-php-ext-install bcmath exif gd intl mysqli pdo_mysql sockets zip

RUN pecl install xdebug && docker-php-ext-enable xdebug

# composer はインストール済にしておくため、get_composer.sh を実行しておく
COPY get_composer.sh /tmp/
RUN set -x && chmod 777 /tmp/get_composer.sh && /tmp/get_composer.sh
# get_composer.sh の内容
#!/bin/sh

EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")

if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
    >&2 echo 'ERROR: Invalid installer signature'
    rm composer-setup.php
    exit 1
fi

php composer-setup.php --quiet
rm composer-setup.php
mv composer.phar /usr/bin/composer
exit 0

php.ini ファイルの作成と内容の記述

docker ディレクトリ内に php.ini ファイルを作成し、下記の内容を記述します。こちらは 最低限のもので、適宜自分で追加しても構いません。

# 以下の場所にファイルを作成&下記内容を記述
htdocs/projectName/docker/php.ini
# php.ini の内容
[PHP]
;;;;;;;;;;;;;;;;;
; Data Handling ;
;;;;;;;;;;;;;;;;;

post_max_size = 20M

;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;

upload_max_filesize = 20M

;;;;;;;;;;;;;;;;;;;
; Module Settings ;
;;;;;;;;;;;;;;;;;;;

[Date]
date.timezone = Asia/Tokyo

[mbstring]
mbstring.language = Japanese

[xdebug]
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9001
xdebug.log="/tmp/xdebug.log"
xdebug.idekey=intellij

db コンテナ(mysql)の設定

最後にデータベースコンテナの設定です。こちらは引数を渡すことでログイン用のユーザ、パスワード、データベースなどが指定できたので、yml ファイルに全て記載しております。

  db:
    container_name: db # コンテナ名
    image: mysql:5.7.23 # 公式 image の指定
    environment:
      - MYSQL_DATABASE=my_app # データベース名
      - MYSQL_USER=my_app     # ユーザ名
      - MYSQL_PASSWORD=secret # パスワード
      - MYSQL_RANDOM_ROOT_PASSWORD=yes
      - TZ=Japan
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    ports:
      - "3306:3306"
    volumes:
      # コンテナを破棄しても DB の内容を残したいので、自分の PC 側に DB ファイルを同期させておく
      - db-my-project:/var/lib/mysql

# TOPレベルにも記載してデータを永続化する
volumes:
  db-my-project:

動作確認用ファイルの作成

コンテナ関連の準備が完了したので、app ディレクトリ内に webroot ディレクトリを作成し、ドキュメントルートを準備します。このディレクトリ内に index.php ファイルを作成し、動作確認用のコードを記述します。内容はなんでもいいですが、試しに下記の内容を記述しました。

# 以下の場所にファイルを作成&下記内容を記述
htdocs/projectName/app/webroot/index.php
<?php
echo 'Hello World';

Docker の立ち上げ

以下のコマンドを docker ディレクトリ内で実行してみてください。コマンドラインに色々なものが流れてきて、落ち着いたら http://localhost にアクセスしてみてください。ご自身の PC 環境下、ドキュメントルートに指定した index.php フィルが実行されるかと思います。

cd htdocs/projectName/docker
docker-compose up

Let’s Access!!

http://localhost

終わりに

今回の記事は、お問い合わせから質問がきたことをキッカケに書いてみました。過去に Vagrant を使った開発環境環境を書いていたのですが、その記事は古かったので「いつか Docker 編を書きたい」と思っており、このタイミングになりました。キッカケに感謝。

こんな感じで CakePHP に関する質問であれば大抵のことは何らかのリアクションができると思うので、皆様どうぞ遠慮なくお問い合わせより質問いただければと思います。

Enjoy!!