docker-compose＋MySQL8（8.0.18）＋php-fpm(7.4)のPDOでcould not find driverが発生

# docker-compose＋MySQL8（8.0.18）＋php-fpm(7.4)のPDOでcould not find driverが発生

## 環境情報

- MacOS X 10.15.1（19B88）
- Docker 19.03.5
- nginx 1.17.6
- php-fpm 7.4.0
- MySQL 8.0.18

## Mac側のディレクトリ構成

```shell-session
.
├── docker-compose.yml
├── mysql
│   └── init
│       ├── 10_ddl.sql
│       ├── 20_data_load.sh
│       └── data.csv
├── php
│   └── src
│       ├── html
│       └── index.php
└── web
    └── conf
        └── default.conf
```

## 各ファイルの内容

### docker-compose.yml

```yaml
version: '3'
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    depends_on:
      - app
    volumes:
      - ./web/conf/default.conf:/etc/nginx/conf.d/default.conf
  app:
    #https://hub.docker.com/_/php/
    image: php:7.4.0-fpm
    volumes:
      - ./php/src:/var/www/
  mysql:
    #https://hub.docker.com/_/mysql
    #image: mysql:8.0.18 <-- latestだとこのバージョン（2019/12/13時点）
    image: mysql:latest
    environment:
      #イメージの起動時に作成するデータベースの名前
      MYSQL_DATABASE: yudb
      #このユーザはMYSQL_DATABASE変数で指定されたデータベースに対してスーパーユーザとしての権限(GRANT ALL)を保持する
      MYSQL_USER: mysqluser
      #MYSQL_USERのパスワード
      MYSQL_PASSWORD: mysqluser00
      # MySQLにおけるスーパーユーザであるrootアカウントに設定するためのパスワード
      MYSQL_ROOT_PASSWORD: mysqluser000
    ports:
      - "3306:3306"
    volumes:
      # /var/lib/mysqlをvolumesで追加する意図として、MySQLのデータベースに加えた変更を永続化するため。
      # 例えば、「yudb」という名前でデータベースを作成し、mysqluserを作成し、DDLを流してテーブルを作成し、
      # LOAD DATA LOCAL INFILEによってCSVファイルで初期データをロードした場合、/var/lib/mysqlをvolumesに指定しないと、
      # MySQLのコンテナを起動するたびに初期化処理が走るようになります。
      #- ./mysql/var_lib_mysql:/var/lib/mysql
      # /docker-entrypoint-initdb.d/配下は、Dockerコンテナが初回起動（初期化）される際に１度だけ実行されるスクリプトなどを配置
      # *.sh / *.sql / *.sql.gzの拡張子のファイルはファイル名の昇順に実行される。
      - ./mysql/init:/docker-entrypoint-initdb.d
```

### default.conf

```config
server {
  listen 80;
  server_name 127.0.0.1;

  #ドキュメントルートの設定
  root  /var/www/;
  index index.php index.html index.htm;

  location / {
    # 指定された順序でfileやdirの存在を確認し、最初に見つかったものを返却する。
    # いずれも無かった場合は、最後に指定されたパスに遷移する。
    try_files $uri $uri/ /index.php$is_args$args;
  }

  location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    # php-fpmのコンテナである「app」のポート9000に対してリクエストのパスを設定
    fastcgi_pass   app:9000;
    # 1台のサーバーでnginx+php-fpmを動作させる場合、Unixソケットの方が高速に動作するため、
    # 以下の設定をするが、今回はnginxとphp-fpmを別のコンテナにしているため、ポートでの接続となっている。
    # fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock
    fastcgi_index  index.php;
    include        fastcgi_params;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param  PATH_INFO $fastcgi_path_info;
  }

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

### 10_ddl.sql

```sql
set global local_infile = 1;

create table if not exists m_sample(
  `code` char(3) not null,
  `name` varchar(80) not null,
  primary key(`code`)
) engine=innodb default charset=utf8;
```

### 20_data_load.sh

```shell
mysql -umysqluser -pmysqluser00 yudb --local-infile=1 -e "LOAD DATA LOCAL INFILE '/docker-entrypoint-initdb.d/data.csv' INTO TABLE m_sample FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\n'"
```

### data.csv

```csv
"001","test001"
"002","test002"
"003","test003"
"004","test004"
```

### index.php

```php
<?php

$dsn = 'mysql:host=mysql;dbname=yudb';
$user = 'mysqluser';
$password = 'mysqluser00';

try {
  $dbh = new PDO($dsn, $user, $password);

  $sql = "select * from m_sample";

  foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
    var_dump($row);
  }
} catch (PDOException $e) {
  var_dump($e);
}
```

## docker-composeでコンテナを起動してPHPのページにアクセスするとエラーが発生

```shell-session
$ docker-compose up -d
Creating network "php-docker_default" with the default driver
Creating php-docker_mysql_1 ... done
Creating php-docker_app_1   ... done
Creating php-docker_web_1   ... done
```

http://127.0.0.1
にアクセスすると以下のエラーが発生します。
**var_dumpでPDOExceptionを出力した結果**

```php
object(PDOException)#2 (8) {
["message":protected]=> string(21) "could not find driver" ["string":"Exception":private]=> string(0) ""
["code":protected]=> int(0)
["file":protected]=> string(18) "/var/www/index.php"
["line":protected]=> int(8)
["trace":"Exception":private]=> array(1) {
  [0]=> array(6) {
    ["file"]=> string(18) "/var/www/index.php"
    ["line"]=> int(8)
    ["function"]=> string(11) "__construct" ["class"]=> string(3) "PDO"
    ["type"]=> string(2) "->"
    ["args"]=> array(3) {
        [0]=> string(28) "mysql:host=mysql;dbname=yudb"
        [1]=> string(9) "mysqluser"
        [2]=> string(11) "mysqluser00"
    }
  }
}
["previous":"Exception":private]=> NULL
["errorInfo"]=> NULL }
```

## エラーの対応

PDOで`could not find driver`が出る原因は、そのままの理由で、PDOのドライバとしてMySQLがセットアップされていないために発生しているようです。
`phpinfo();`を実行して確認すると、PDOのDriverとしては`sqlite`しか含まれていないことがわかります。

<img width="943" alt="スクリーンショット 2019-12-17 7.54.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/28c8e64a-5c9e-55c6-c09b-5eec83021b9f.png">

PHPのコンテナにアクセスして、コマンドで確認することもできます。

```shell-session
$ docker-compose exec app bash
root@53a32e821990:/var/www/html# php -m | grep pdo
pdo_sqlite
```

Mac側の環境で、`Dockerfile`をPHP用のディレクトリ配下に追加

```shell-session
.
├── docker-compose.yml
├── mysql
│   └── init
│       ├── 10_ddl.sql
│       ├── 20_data_load.sh
│       └── data.csv
├── php
│   ├── Dockerfile  ←　これを追加
│   └── src
│       ├── html
│       └── index.php
└── web
    └── conf
        └── default.conf
```

Dockerfileの内容を以下のように指定。

```dockerfile
FROM php:7.4.0-fpm

RUN apt-get update \
    && docker-php-ext-install pdo_mysql
```

docker-compose.ymlファイルのPHP設定部分を修正。

```yaml
  app:
    #https://hub.docker.com/_/php/
    #image: php:7.4.0-fpm  ←　ここをコメントアウトして
    build: ./php  # これを追加
    volumes:
      - ./php/src:/var/www/
```

ここまでの修正を加えたら、一度コンテナを落として、再度起動します。

```shell-session
$ docker-compose down
Stopping php-docker_web_1   ... done
Stopping php-docker_app_1   ... done
Stopping php-docker_mysql_1 ... done
Removing php-docker_web_1   ... done
Removing php-docker_app_1   ... done
Removing php-docker_mysql_1 ... done
Removing network php-docker_default

$ docker-compose up -d
Creating network "php-docker_default" with the default driver
Building app
Step 1/2 : FROM php:7.4.0-fpm
 ---> e1131e2249f6
Step 2/2 : RUN apt-get update     && docker-php-ext-install pdo_mysql
 ---> Running in cd1a8971f78b
Get:1 http://deb.debian.org/debian buster InRelease [122 kB]
Get:3 http://deb.debian.org/debian buster-updates InRelease [49.3 kB]
Get:2 http://security-cdn.debian.org/debian-security buster/updates InRelease [65.4 kB]
Get:4 http://deb.debian.org/debian buster/main amd64 Packages [7908 kB]
Get:5 http://security-cdn.debian.org/debian-security buster/updates/main amd64 Packages [162 kB]
Get:6 http://deb.debian.org/debian buster-updates/main amd64 Packages [5792 B]
Fetched 8312 kB in 10s (842 kB/s)
Reading package lists...
Configuring for:
PHP Api Version:         20190902
Zend Module Api No:      20190902
Zend Extension Api No:   320190902
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for a sed that does not truncate output... /bin/sed
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether cc accepts -g... yes
checking for cc option to accept ISO C89... none needed
checking how to run the C preprocessor... cc -E
checking for icc... no
checking for suncc... no
checking for system library directory... lib
checking if compiler supports -R... no
checking if compiler supports -Wl,-rpath,... yes
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking target system type... x86_64-pc-linux-gnu
checking for PHP prefix... /usr/local
checking for PHP includes... -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib
checking for PHP extension directory... /usr/local/lib/php/extensions/no-debug-non-zts-20190902
checking for PHP installed headers prefix... /usr/local/include/php
checking if debug is enabled... no
checking if zts is enabled... no
checking for gawk... no
checking for nawk... nawk
checking if nawk is broken... no
checking for MySQL support for PDO... yes, shared
checking for the location of libz... no
checking for MySQL UNIX socket location... 
checking for PDO includes... /usr/local/include/php/ext
checking for a sed that does not truncate output... /bin/sed
checking for ld used by cc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for /usr/bin/ld option to reload object files... -r
checking for BSD-compatible nm... /usr/bin/nm -B
checking whether ln -s works... yes
checking how to recognize dependent libraries... pass_all
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking dlfcn.h usability... yes
checking dlfcn.h presence... yes
checking for dlfcn.h... yes
checking the maximum length of command line arguments... 1572864
checking command to parse /usr/bin/nm -B output from cc object... ok
checking for objdir... .libs
checking for ar... ar
checking for ranlib... ranlib
checking for strip... strip
checking if cc supports -fno-rtti -fno-exceptions... no
checking for cc option to produce PIC... -fPIC
checking if cc PIC flag -fPIC works... yes
checking if cc static flag -static works... yes
checking if cc supports -c -o file.o... yes
checking whether the cc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... no

creating libtool
appending configuration tag "CXX" to libtool
configure: patching config.h.in
configure: creating ./config.status
config.status: creating config.h
/bin/bash /usr/src/php/ext/pdo_mysql/libtool --mode=compile cc -I/usr/local/include/php/ext -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/src/php/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/src/php/ext/pdo_mysql/include -I/usr/src/php/ext/pdo_mysql/main -I/usr/src/php/ext/pdo_mysql -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib  -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H  -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64   -c /usr/src/php/ext/pdo_mysql/pdo_mysql.c -o pdo_mysql.lo 
mkdir .libs
 cc -I/usr/local/include/php/ext -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/src/php/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/src/php/ext/pdo_mysql/include -I/usr/src/php/ext/pdo_mysql/main -I/usr/src/php/ext/pdo_mysql -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c /usr/src/php/ext/pdo_mysql/pdo_mysql.c  -fPIC -DPIC -o .libs/pdo_mysql.o
/bin/bash /usr/src/php/ext/pdo_mysql/libtool --mode=compile cc -I/usr/local/include/php/ext -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/src/php/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/src/php/ext/pdo_mysql/include -I/usr/src/php/ext/pdo_mysql/main -I/usr/src/php/ext/pdo_mysql -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib  -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H  -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64   -c /usr/src/php/ext/pdo_mysql/mysql_driver.c -o mysql_driver.lo 
 cc -I/usr/local/include/php/ext -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/src/php/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/src/php/ext/pdo_mysql/include -I/usr/src/php/ext/pdo_mysql/main -I/usr/src/php/ext/pdo_mysql -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c /usr/src/php/ext/pdo_mysql/mysql_driver.c  -fPIC -DPIC -o .libs/mysql_driver.o
/bin/bash /usr/src/php/ext/pdo_mysql/libtool --mode=compile cc -I/usr/local/include/php/ext -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/src/php/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/src/php/ext/pdo_mysql/include -I/usr/src/php/ext/pdo_mysql/main -I/usr/src/php/ext/pdo_mysql -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib  -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H  -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64   -c /usr/src/php/ext/pdo_mysql/mysql_statement.c -o mysql_statement.lo 
 cc -I/usr/local/include/php/ext -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/src/php/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/src/php/ext/pdo_mysql/include -I/usr/src/php/ext/pdo_mysql/main -I/usr/src/php/ext/pdo_mysql -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c /usr/src/php/ext/pdo_mysql/mysql_statement.c  -fPIC -DPIC -o .libs/mysql_statement.o
/bin/bash /usr/src/php/ext/pdo_mysql/libtool --mode=link cc -DPHP_ATOM_INC -I/usr/src/php/ext/pdo_mysql/include -I/usr/src/php/ext/pdo_mysql/main -I/usr/src/php/ext/pdo_mysql -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib  -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H  -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64  -Wl,-O1 -Wl,--hash-style=both -pie  -o pdo_mysql.la -export-dynamic -avoid-version -prefer-pic -module -rpath /usr/src/php/ext/pdo_mysql/modules  pdo_mysql.lo mysql_driver.lo mysql_statement.lo 
cc -shared  .libs/pdo_mysql.o .libs/mysql_driver.o .libs/mysql_statement.o   -Wl,-O1 -Wl,--hash-style=both -Wl,-soname -Wl,pdo_mysql.so -o .libs/pdo_mysql.so
creating pdo_mysql.la
(cd .libs && rm -f pdo_mysql.la && ln -s ../pdo_mysql.la pdo_mysql.la)
/bin/bash /usr/src/php/ext/pdo_mysql/libtool --mode=install cp ./pdo_mysql.la /usr/src/php/ext/pdo_mysql/modules
cp ./.libs/pdo_mysql.so /usr/src/php/ext/pdo_mysql/modules/pdo_mysql.so
cp ./.libs/pdo_mysql.lai /usr/src/php/ext/pdo_mysql/modules/pdo_mysql.la
PATH="$PATH:/sbin" ldconfig -n /usr/src/php/ext/pdo_mysql/modules
----------------------------------------------------------------------
Libraries have been installed in:
   /usr/src/php/ext/pdo_mysql/modules

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH' environment variable
     during linking
   - use the `-Wl,--rpath -Wl,LIBDIR' linker flag
   - have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------

Build complete.
Don't forget to run 'make test'.

Installing shared extensions:     /usr/local/lib/php/extensions/no-debug-non-zts-20190902/
find . -name \*.gcno -o -name \*.gcda | xargs rm -f
find . -name \*.lo -o -name \*.o | xargs rm -f
find . -name \*.la -o -name \*.a | xargs rm -f
find . -name \*.so | xargs rm -f
find . -name .libs -a -type d|xargs rm -rf
rm -f libphp.la      modules/* libs/*
Removing intermediate container cd1a8971f78b
 ---> fa838ec754af
Successfully built fa838ec754af
Successfully tagged php-docker_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating php-docker_mysql_1 ... done
Creating php-docker_app_1   ... done
Creating php-docker_web_1   ... done
```

起動したらPHPの`index.php`のページへアクセスしてDBアクセスが正常に動作しているか確認する。
http://127.0.0.1

PDOのエラーが発生することはなく、正常にDBアクセスできることを確認。
以下取得結果

```
array(2) { ["code"]=> string(3) "001" ["name"]=> string(7) "test001" } array(2) { ["code"]=> string(3) "002" ["name"]=> string(7) "test002" } array(2) { ["code"]=> string(3) "003" ["name"]=> string(7) "test003" } array(2) { ["code"]=> string(3) "004" ["name"]=> string(7) "test004" }
```

`phpinfo()`の結果も合わせて確認すると、PDOのMySQLドライバがセットアップされていることを確認できます。

<img width="952" alt="スクリーンショット 2019-12-17 8.10.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/7bd20775-3ccd-ef81-b360-d7ae85285f9e.png">

コマンドでの確認方法はこちら

```shell-session
$ docker-compose exec app bash
root@d80098dfe5af:/var/www/html# php -m | grep pdo
pdo_mysql
pdo_sqlite
```

PDOってデフォルトでコンテナに入ってないというのはアセりました。
コンテナって本当に最小構成で作られているマイクロサービスなんですねぇ。。。