Docker ComposeでNginx+PHP+MySQL+Redis環境を構築

# Docker ComposeでNginx+PHP+MySQL+Redis環境を構築

2021年現在最新のNginx+PHP+MySQL+Redis環境を構築しました
各サービス項目の説明まで

##Docker構成
| コンテナ | ミドルウェア | 
|:--|:--|
| Web | Nginx |
| PHP | PHP8 |
| DB | MySQL |
| Redis | Redis |

## ディレクトリ構成

```
docker_lamp/  
|-- mysql  
|   |-- dump  
|   |   `-- setup.sql  
|   |-- logs
|   |-- mount  
|   `-- settings
|       `-- my.cnf
|-- php  
|   |-- logs  
|   |-- settings  
|   |   |-- php.dev.ini
|   |   |-- php.prod.ini
|   |   |-- www.dev.conf
|   |   `-- www.prod.conf  
|   `-- Dockerfile
|-- project  
|   `-- html
|   	 `-- public 
|   	     `-- index.php
|-- redis  
|   `-- mount  
|-- web  
|   |-- logs  
|   `-- settings
|       |-- default.conf
|       `-- nginx.conf
`-- docker-compose.yml
```

## docker-compose.yaml

PHPはDockerfileを使って自分でイメージを作成
他は公式イメージを利用

### 一旦全体

```
version: '3'
services:
  web:
    image: nginx:alpine
    volumes:
      - "./web/settings/default.conf:/etc/nginx/conf.d/default.conf"
      - "./web/settings/nginx.conf:/etc/nginx/nginx.conf"
      - "./project/html:/var/www/html"
      - "./web/logs:/var/log/nginx"
    ports:
      - "8080:80"
    restart: always
    depends_on:
      - php
      - mysql
      - redis
  php:
    build: ./php
    volumes:
      - "./project/html:/var/www/html"
    restart: always
  mysql:
    image: mysql:8.0
    ports:
      - "13306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: p@ssw0rd
      MYSQL_DATABASE: test
      MYSQL_USER: dev
      MYSQL_PASSWORD: dev
      TZ: Asia/Tokyo
    volumes:
      - "./mysql/mount:/var/lib/mysql"
      - "./mysql/settings/my.cnf:/etc/mysql/conf.d/my.cnf"
      - "./mysql/dump/setup.sql:/docker-entrypoint-initdb.d/dump.sql"
      - "./mysql/logs:/var/log/mysql"
    restart: always
  redis:
    image: redis:alpine
    ports:
      - "16379:6379"
    volumes:
      - "./redis/mount:/data"
```

## 各項目説明

### Nginx

1. 外部アクセス用に8080ポートを公開
2. 設定ファイル、プロジェクト、ログはvolumesで同期

※ 設定ファイルは後半に紹介

```
  web:
    image: nginx:alpine
    volumes:
      - "./web/settings/default.conf:/etc/nginx/conf.d/default.conf"
      - "./web/settings/nginx.conf:/etc/nginx/nginx.conf"
      - "./project/html:/var/www/html"
      - "./web/logs:/var/log/nginx"
    ports:
      - "8080:80"
    restart: always
    depends_on:
      - php
      - mysql
      - redis
```

### PHP

プロジェクトはvolumesで同期

```
  php:
    build: ./php
    volumes:
      - "./project/html:/var/www/html"
```

#### Dockerfile

1. PHP用イメージ
2. Redis、MySQLを利用するためdocker-php-ext-installを使いPHP拡張をインストール
3. 設定ファイルをCOPYを使い反映

※ 設定ファイルは後半に紹介

```
FROM php:8.0-fpm

RUN apt-get update && apt-get install -y git

RUN git clone https://github.com/phpredis/phpredis.git /usr/src/php/ext/redis
RUN docker-php-ext-install mysqli redis

COPY settings/php.dev.ini /usr/local/etc/php/conf.d/php.ini
COPY settings/www.dev.conf /usr/local/etc/php-fpm.d/www.conf
``` 

### MySQL

1. 外部アクセス用に13306ポートを公開
2. environmentの設定を行うとコンテナ作成時に自動でパスワード作成、データベース作成などが自動で実行（今回はルートパスワード設定、データベース、ユーザー、タイムゾーンを設定）
3. リソースはvolumesを使いmount以下に同期（Dockerを停止してもデータを残す）
4. 設定ファイル、ログはvolumesで同期
5. 作成時に実行させたいSQLはvolumesで `/docker-entrypoint-initdb.d/dump.sql` に同期すると自動で実行


```
  mysql:
    image: mysql:8.0
    ports:
      - "13306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: p@ssw0rd
      MYSQL_DATABASE: test
      MYSQL_USER: dev
      MYSQL_PASSWORD: dev
      TZ: Asia/Tokyo
    volumes:
      - "./mysql/mount:/var/lib/mysql"
      - "./mysql/settings/my.cnf:/etc/mysql/conf.d/my.cnf"
      - "./mysql/dump/setup.sql:/docker-entrypoint-initdb.d/dump.sql"
      - "./mysql/logs:/var/log/mysql"
    restart: always
```   

### Redis

1. 外部アクセス用に16379ポートを公開
2. リソースはvolumesを使いmount以下に同期（Dockerを停止してもデータを残す）

```
  redis:
    image: redis:alpine
    ports:
      - "16379:6379"
    volumes:
      - "./redis/mount:/data"
```

## 設定ファイル

デフォルトで動かしてもいいですが、ECS運用も考慮して設定ファイルを用意

### Nginx

#### default.conf

※ 構築時にCodeigniter, FuelPHP環境の構築を考えていたのでこの形になった
ポイントは `fastcgi_pass php:9000;` ホストをdocker-composeのサービス名（php）に変更

```
server {
    listen  80 default;
    server_name  localhost;

    charset utf-8;

    root /var/www/html/public;
    index index.php index.html index.htm;

    location / {
        index index.php index.html index.htm;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location /admin {
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
        index index.php index.html index.htm;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location /reploxy {
        internal;
        resolver 8.8.8.8;
        set $reproxy $upstream_http_x_reproxy_url;
        proxy_pass $reproxy;
        proxy_set_header Authorization "";
    }

    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param FUEL_ENV "development";
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
        include fastcgi_params;
    }
}
```

#### nginx.conf

ログの場所など指定

```
user  nginx nginx;
worker_processes  2;
pid  /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

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

    sendfile off;
    tcp_nopush on;
    tcp_nodelay  off;
    keepalive_timeout  30;
    send_timeout 60;

    gzip  on;
    gzip_http_version 1.0;
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_min_length 10000;
    gzip_types  text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_disable "MSIE [1-6] \.";

    include /etc/nginx/conf.d/*.conf;
}
```

### PHP

php.ini、www.conf設定ファイルは以下サイトを参考にさせていただきました

https://thoames.hatenadiary.jp/entry/2020/05/12/073656

https://qiita.com/ucan-lab/items/0d74378e1b9ba81699a9

### MySQL

#### my.cnf

文字コード、ログの場所など指定

```
[mysqld]
# 文字コード/照合順序の設定
character-set-server = utf8mb4
collation-server = utf8mb4_bin

# タイムゾーンの設定
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# デフォルト認証プラグインの設定
default-authentication-plugin = mysql_native_password

# エラーログの設定
log-error = /var/log/mysql/mysql-error.log

# スロークエリログの設定
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 5.0
log_queries_not_using_indexes = 0

# 実行ログの設定
general_log = 1
general_log_file = /var/log/mysql/mysql-query.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4
```

## 実行ファイル

### PHP

#### index.php

サンプルでMySQLとRedisを使う
ポイントは接続ホストにDockerのサービス名を使う`mysql` `redis`

```PHP
<?php

// MySQLテスト
echo "MySQLテスト<br />";
$mysqli = mysqli_connect("mysql", "dev", "dev", "test");
$query = "SELECT * FROM `user`";
$result = $mysqli->query($query);
$rows = array();
while ($row = $result->fetch_assoc()) {
    $rows[] = $row;
}
var_dump($rows);

echo "<hr />";

// Redisテスト
echo "Redisテスト<br />";
$redis = new Redis();
$redis->connect("redis", 6379);
$redis->set('key', 'value');
echo $redis->get('key');
```

### MySQL

#### setup.sql

サンプル用のSQL `"./mysql/dump/setup.sql:/docker-entrypoint-initdb.d/dump.sql"` の設定でコンテナ作成時にクエリ実行

```SQL
use test;

DROP TABLE IF EXISTS `test`;

CREATE TABLE `user` (
  `name` VARCHAR(255) NOT NULL COMMENT 'Name',
  `age` INT(10) unsigned NOT NULL COMMENT 'Age',
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='テスト用';

INSERT INTO `user` VALUES ("TAKASHI", 34, "0000-01-01", "9999-12-31");
```

## docker-compose 実行

ビルドして実行

```
$ docker-compose up --build
```

## 確認

`http://127.0.0.1:8080/` にアクセス、表示を確認

![スクリーンショット 2021-05-11 9.25.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59264/a067b148-e97d-4a0c-5476-9bfaa22c0f91.png)

---

次回はこの環境下にComposer、Xdebug、PHPフレームワーク（Codeigniter予定）、UnitTest等を使えるようしていきます。
いいね！と思ったら フォロー、LGTM お願いします :clap::clap::clap:

【PR】プログラミング新聞リリースしました！ → https://pronichi.com
【PR】週末ハッカソンというイベントやってます！ → https://weekend-hackathon.toyscreation.jp/about/

<!--
https://codeaid.jp/blog/docker-lamp-nginx/

https://yama-itech.net/construct-lamp-with-docker

https://qiita.com/tomokei5634/items/ff0784e88b026b530e3e

https://www.kwbtblog.com/entry/2019/09/05/054017

https://tt-computing.com/docker-nginx1_19-php7_4-mariadb10_5

https://github.com/nanoninja/docker-nginx-php-mysql

https://github.com/kawazu255/docker-lamp/tree/master/db

https://github.com/nnahito/LAMP_environment_by_docker-compose/blob/master/docker-compose.yml

-->