dockerを使ってサクッとPHPとMySQL環境を手に入れる

ちょっと何かを試したいときdockerは便利です。
環境構築と破棄を気軽にできるからです。
というわけで今回は、dockerでサクッと作れるPHPとMySQL環境を作ってみようと思います。
何かを検証したいときにこういった環境を手軽に作れると時短になるので便利です。

# ディレクトリ構成
```
./
│  docker-compose.yml
│
├─app
│  └─htdocs
│          index.php
│
└─docker
    ├─db
    │  │  Dockerfile
    │  │  my.cnf
    │  │
    │  └─init
    │          1.create-db.sql
    │          2.grant-user.sql
    │          3.create-table.sql
    │          4.insert-data.sql
    │
    └─web
            Dockerfile
            php.ini
```

# 各ファイルの説明
```docker-compose.yml
version: '3'

services:
  # webコンテナの設定
  web:
    # ./docker/web/Dockerfileを参照してbuildする
    build: ./docker/web
    # コンテナにwebという名前を付ける
    container_name: web
    # 環境変数の設定(Timezoneを日本に設定)
    environment:
      TZ: "Asia/Tokyo"
    # portのマッピング(ホストOSの80番ポートとwebコンテナの80番ポート)
    ports:
      - "80:80"
    # ファイルシステムのマッピング(ホストOSの./appとwebコンテナの/app)
    # ファイルシステムのマッピング(ホストOSの./docker/web/php.iniとwebコンテナの/usr/local/etc/php/conf.d/php.ini)
    volumes:
      - "./app:/app"
      - "./docker/web/php.ini:/usr/local/etc/php/conf.d/php.ini"
    # dbコンテナを先に起動する
    depends_on:
      - db

  # dbコンテナの設定
  db:
    # ./docker/db/Dockerfileを参照してbuildする
    build: ./docker/db
    # コンテナにdbという名前を付ける
    container_name: db
    # 環境変数の設定(mysqlのrootパスワード設定)
    # 環境変数の設定(Timezoneを日本に設定)
    environment:
      MYSQL_ROOT_PASSWORD: "your_root_password"
      TZ: "Asia/Tokyo"
    # portのマッピング(ホストOSの3306番ポートとdbコンテナの3306番ポート)
    ports:
      - 3306:3306
    # ファイルシステムのマッピング(ホストOSのmysql_data(後述)とdbコンテナの/var/lib/mysql)
    # ファイルシステムのマッピング(ホストOSの./docker/db/initとdbコンテナの/docker-entrypoint-initdb.d)
    # ↑ちなみにコンテナ起動時に/docker-entrypoint-initdb.dの中にあるsqlファイルを自動実行する
    # ファイルシステムのマッピング(ホストOSの./docker/db/my.cnfとdbコンテナの/etc/mysql/conf.d/my.cnf)
    volumes:
      - "mysql_data:/var/lib/mysql"
      - "./docker/db/init:/docker-entrypoint-initdb.d"
      - "./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf"
    # コンテナが停止すると自動的に再起動する
    restart: always

# ストレージの設定
volumes:
  # ホストOSにmysql_dataという名前を持ったストレージを作る
  mysql_data:
    driver: local

```

```Dockerfile:docker/db/Dockerfile
# mysqlのバージョン8を使用する
FROM mysql:8
```

```my.cnf:docker/db/my.cnf
# mysqlサーバーの設定
[mysqld]
# 文字コードをutf8mb4に設定
character-set-server = utf8mb4
collation-server = utf8mb4_bin

# Timezone設定
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# 認証方式をmysql_native_passwordに変更
default-authentication-plugin = mysql_native_password

# mysql全体の設定?
[mysql]
# 文字コードをutf8mb4に設定
default-character-set = utf8mb4

[client]
# 文字コードをutf8mb4に設定
default-character-set = utf8mb4
```

```sql:docker/db/init/1.create-db.sql
-- example_dbというデータベースを作成
CREATE DATABASE example_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin;
```

```sql:docker/db/init/2.grant-user.sql
-- example_userというユーザーを作成
CREATE USER 'example_user'@'%' IDENTIFIED BY 'example_pass';
-- example_dbに対して全ての権限を付与する
GRANT ALL PRIVILEGES ON example_db.* TO 'example_user'@'%' WITH GRANT OPTION;
-- 設定の反映
FLUSH PRIVILEGES;
```

```sql:docker/db/init/3.create-table-users.sql
USE example_db;

-- examplesというテーブルを作成
CREATE TABLE `examples` (
  `id`           INT NOT NULL AUTO_INCREMENT,
  `name`         VARCHAR(255) NOT NULL,
  CONSTRAINT PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;
```

```sql:docker/db/init/4.insert-data-users.sql
USE example_db;

SET NAMES utf8mb4;

START TRANSACTION;

-- examplesテーブルにデータ登録
INSERT INTO examples VALUES (NULL, 'example_name1');
INSERT INTO examples VALUES (NULL, 'example_name2');
INSERT INTO examples VALUES (NULL, '日本語テスト');

COMMIT;
```

```Dockerfile:docker/web/Dockerfile
# phpバージョン8でapacheを使用
FROM php:8-apache

# 環境変数の設定(Timezoneを日本に設定)
ENV TZ Asia/Tokyo
# 環境変数の設定(DocumentRootの設定)
ENV APACHE_DOCUMENT_ROOT /app/htdocs

# Timezoneを日本に設定
RUN ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime \
  && echo ${TZ} > /etc/timezone

# 必要なエクステンションのインストール
RUN apt-get update \
  && docker-php-ext-install pdo_mysql

# DocumentRootの設定
RUN sed -ri -e \
  's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' \
  /etc/apache2/sites-available/*.conf

# DocumentRootの設定
RUN sed -ri -e \
  's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' \
  /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
```

```php.ini
[PHP]
# Timezoneを日本に設定
date.timezone = "Asia/Tokyo"
```

```php:app/htdocs/index.php
<?php
$username = "example_user";
$password = "example_pass";
$hostname = "db";
$db = "example_db";
// データベース接続
$pdo = new PDO("mysql:host={$hostname};dbname={$db};charset=utf8", $username, $password);

// SQLを実行して結果を画面に表示
$sql = "SELECT * FROM examples";
$stmt = $pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
  echo "id: {$row["id"]}, name: {$row["name"]}<br/>\n";
}
```

# 起動方法
```bash
$ docker-compose up -d --build
Creating network "test-docker_default" with the default driver
Building db
# --- 途中省略 ---
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Creating db ... done
Creating web ... done

```

# 確認方法
```bash
$ docker-compose ps -a
Name              Command               State                 Ports
---------------------------------------------------------------------------------
db     docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp, 33060/tcp
web    docker-php-entrypoint apac ...   Up      0.0.0.0:80->80/tcp
```

ちゃんと起動できています。

# 検証
WEBブラウザで[http://localhost]にアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/29aceb66-01db-713a-791c-bd8a6af24dad.png)

ちゃんとデータベースから値を取得して表示が出来ました！

# 停止方法
```bash
$ docker-compose down
$ docker-compose down -v
```

``docker-compose down``の方は``mysql_data``を残して停止します。（データ永続化出来ます。）
``docker-compose down -v``の方は``mysql_data``を削除して停止します。（データが消えます。）

# repository
https://github.com/y-dobashi/docker-php-mysql