前回の記事からの続きなので下記もよろしければ!
さらっとDocker入門

PHP+Apacheコンテナの作成

前回のおさらいですが、Apacheコンテナを作るには次のコマンドを実行しました。

$ docker container run --name test1 -d -p 8080:80 -v /Users/ユーザー名/htdocs:/usr/local/apache2/htdocs httpd

PHPを実行できるようにするには、最初からPHPをApacheがセットになったイメージがあるのでそれを使うと早いです。
現在(2021年3月)の最新版はPHP 8.0.3なのでhttpdの部分をphp:8.0.3-apacheにして、ドキュメントルートも/var/www/htmlに変更します。

$ docker container run --name test1 -d -p 8080:80 -v /Users/ユーザー名/htdocs:/var/www/html php:8.0.3-apache

ホストでしていしたディレクトリ(/Users/ユーザー名/htdocs)に次のPHPファイルを作ってください。

index.php

<?php
phpinfo();

ブラウザでhttp://localhost:8080にアクセスしてみましょう。
PHPの情報が表示されていれば成功です。
とりあえずPHPを動かすだけなら簡単ですね。

php.iniの設定

例えばPHPの設定はphp.iniファイルで設定するのですが、今の状態だと何も設定していない状態なので設定してみましょう。

前回のコンテナへのファイルコピーを思い出してください。
コンテナに入っているphp.ini-developmentをホストにコピーしましょう。

$ docker container cp test1:/usr/local/etc/php/php.ini-development ./

php.ini-developmentphp.iniにリネームして必要な設定をしましょう。
ここではtimezoneを設定してみます。

php.ini

date.timezone = "Asia/Tokyo"
$ docker container cp ./php.ini test1:/usr/local/etc/php/

コンテナを再起動してPHPの設定が変更されているのを確認してください。

$ docker container restart test1

ここまでできたら一度コンテナを削除しておいてください。

$ docker stop test1
$ docker rm test1

Dockerファイルの作成

php.iniの設定はできましたが、このままだとコンテナを削除したら再度コピーを実行しなければいけません。
一つのファイルだけならいいのですが、コピーしないといけないファイルが複数あった場合大変だし間違いが起こりそうですよね。

Dockerfileを使用すると決められた手順のコマンドをファイルに保存して実行することができます。
先ほど作成したホスト側のphp.iniと同じディレクトリにDockerfileを下記のように作成してください。

Dockerfile

FROM php:8.0.3-apache
COPY php.ini /usr/local/etc/php/

Dockerfileを使用すると基本的にオリジナルのイメージを作るということになります。
このファイルではphp:8.0.3-apacheをベースに、独自のphp.iniを設定した独自のイメージを作るということですね。

Dockerfileはbuildコマンドを実行することでオリジナルのイメージが生成されます。
下記コマンドはmy_phpというイメージ名で作成するということなのですが、/dockerfile/dir/は先ほど作成したDockerfileファイルのあるディレクトリを指定します。

$ docker build -t my_php /dockerfile/dir/

イメージが作られたか確認してみましょう。

$ docker image ls

作成したイメージを元にコンテナを作成します。

$ docker container run --name test1 -d -p 8080:80 -v /Users/ユーザー名/htdocs:/var/www/html my_php

これでコンテナ作成時にphp.iniが作られた状態になります。

MySQLのコンテナの作成

PHPアプリケーションはデータベースとセットで開発することがほとんどだと思います。
データベースはいろいろありますが、ここではMySQLを使えるようにしてみましょう。
PHPとは別にMySQL用のコンテナを作成しPHPと連携できるようにします。
連携する前にMySQL単独で使用してみましょう。

現時点での最新バージョンは「8.0.2」なので、イメージ名はmysql:8.0.23にします。
MYSQL_ROOT_PASSWORDには任意のパスワードを入力してください。

$ docker container run --name mysql_test1 -e MYSQL_ROOT_PASSWORD=password -d mysql:8.0.23

コンテナに入ってMySQLを使用できるか確認しましょう。

$ docker container exec -it mysql_test1 /bin/bash

MySQLにログイン

# mysql -u root -p

操作できるか確認してみましょう。

mysql> show databases;

問題なかったら終了

mysql> show exit;

コンテナからも抜けます。

# exit

MySQLコンテナにはその他にもオプションがあります。
MYSQL_DATABASEを指定するとコンテナ作成時にデータベースを作成することができます。

$ docker container run --name mysql_test1 -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=test_db -d mysql:8.0.23

その他設定に書くようなオプションは引数として追加することができます。

文字コード –character-set-server=utf8mb4
照合順序 –collation-server=utf8mb4_unicode_ci

これを全部入れるとこんな感じです。

$ docker container run --name mysql_test1 -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=test_db -d mysql --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

Docker Compose

一つのコンテナを作成するだけでも色々とオプションを指定しないといけなくて結構大変になってきましたね。
Docker Composeを使用すると、コマンドで入力していたのをファイルにまとめて実行することができます。

構築したPHP環境をDocker Composeで作ってみましょう。
dockerというディレクトリを作り、その中にdocker-compose.ymlというファイルを作ります。
さらに同階層にphpデイィレクトリを作り、PHPコンテナ作成時に作ったDockerfilephp.iniを入れます。

このような構造になります。

docker
├ docker-compose.yml
├ htdocs
└ php
  ├ Dockerfile
  └ php.ini

Docker run で実行していたコマンドを docker-compose で書くと次のようになります。

docker-compose.yml

version: "3"

services:
  php:
    container_name: php_test1
    build:
      context: ./php
    ports:
      - 8080:80
    volumes:
      - ./htdocs:/var/www/html
    restart: always

container runと比較してもらうと大体わかるのではないかと思います。
services直下にあるphpはサービス名でコンテナ毎に任意の名称を指定します。
Dockerfileでコンテナを作る場合はbuildcontextにDockerfileの入っているディレクトリを指定します。 restartはコンテナが停止したときの挙動です。alwaysは必ず再起動する設定です。

Docker composeでcontainer runに相当するコマンドはupになります。

$ docker-compose -f /path/to/docker-compose.yml up -d

docker-compose.ymlファイルのあるディレクトリをカレントディレクトリにすることで、ファイルパスのオプションを省略することができます。

$ docker-compose up -d

コンテナの停止・削除はdownを使用します。

$ docker-compose down

コンテナの停止だけしたい場合はstopを使用します。

$ docker-compose stop

MySQLの追加

docker-composeに先ほど作成したMySQLも追加してみましょう。

docker-compose.yml

version: "3"

services:
  php:
    container_name: php_test1
    build:
      context: ./php
    ports:
      - 8080:80
    volumes:
      - ./htdocs:/var/www/html
    restart: always
  db:
    container_name: mysql_test1
    image: mysql:8.0.23
    volumes:
      - ./mysqlvol:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: test_db
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    ports:
      - 3306:3306

変更点はphpと同じように、データの置き場所のvolumesを設定したので、ホストにmysqlvolディレクトリを作成します。
upコマンドでコンテナを作ります。

$ docker-compose up -d

簡単なファイルを作成してPHPから接続できるか確認してみましょう。
MySQLのhostはコンテナ名のmysql_test1になります。

htdocs/index.php

<?php
$dsn      = 'mysql:dbname=test_db;host=mysql_test1;port=3306;';
$user     = 'root';
$password = 'password';

try {
	$db = new PDO($dsn, $user, $password);
	echo '接続成功';
} catch(PDOException $e) {
	echo '接続失敗' . $e->getMessage();
}

http://localhost:8080にアクセスして確認してください。

ボリュームマウント

先ほどのMySQLの設定ではPHPと同じようにバインドマウントでホストと同期したのですが、MySQLのファイルは基本的に直接操作することがないので、あまり同期させる意味はないですよね。
ただコンテナ消したときに消えてしまっては困ります。
そんなときはボリュームマウントという機能を使うことでコンテナとは別の領域にデータを置くことができます。

servicesと同じ並びにvolumesを追記します。
volumesには任意の名前を付けましょう。
あとはvolumesに付けた名前をdbvolumesに指定します。

docker-compose.yml

version: "3"

services:
  php:
    container_name: php_test1
    build:
      context: ./php
    ports:
      - 8080:80
    volumes:
      - ./htdocs:/var/www/html
    restart: always
  db:
    container_name: mysql_test1
    image: mysql:8.0.23
    volumes:
      - mysqlvol1:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: test_db
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    ports:
      - 3306:3306
volumes:
  mysqlvol1:

試しにテーブルを作ってからコンテナを再構築してDBのデータが消えていないことを確認してください。

作成したボリュームは下記で確認することができます。

$ docker volume ls

削除する場合はコンテナなどと同じようにrmを使用します。

$ docker volume rm php_mysqlvol1

容量の確認

ボリュームに限った話ではないですが、そのままにしておくと容量が圧迫するのでこまめに削除しましょう。
下記でどのくらい容量使っているのか確認することができます。

$ docker system df -v

この記事の動画(Youtube)版はこちら!