初心者｜Docker-ComposeでWordPressとMySQLとphpMyAdminのローカル環境の構築

# プロローグ
3分でローカル環境にWordPressを導入する方法について記載します。
準備までは３ステップです。

## 概要
* Docker および Docker-Compose を使ってローカル環境へ簡単にWordPressを導入します。
* WordPress と データベース（MySQL） に加え、phpMyAdmin も導入しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283603/6d5ecee0-dca8-0983-d91c-aacee3cbd27b.png)

## こんな人のお役に立てれば...
* Docker 初心者の方
* WordPressでWEBサイト構築をしている方

## 前提条件
### Docker がローカルにインストールされている 
使用するOSによってインストール方法が異なります。
「[docker インストール方法](https://www.google.com/search?q=docker+インストール方法&oq=docker+インストール方法)」などでググってインストール方法をご確認ください。
### Docker Compose がローカルにインストールされている
[Docker Compose のインストール](http://docs.docker.jp/compose/install.html)を参照いただくか「[docker-compose インストール方法](https://www.google.com/search?q=docker-compose+インストール方法)」などでググってインストール方法をご確認ください。

# Docker と Docker-Compose で WordPress を導入する方法
## １） 作業フォルダの作成
```terminal:terminal
$ mkdir project && cd project
```
任意のディレクトリを作成（例：project） && 作成したディレクトリへ移動
（Winであれば右クリック→新規フォルダで作成）

## 2） ファイル「docker-compose.yml」の作成
```terminal:terminal
$ touch docker-compose.yml
```
ファイル名「docker-compose.yml」のファイルを作成
（Winであればメモ帳を開いて「docker-compose.yml」で保存）

※ファイル「docker-compose.yml」があるディレクトリを、今後は「作業フォルダ」と記します。

## 3） 「docker-compose.yml」 にコピペ
```yaml:docker-compose.yml
version: "3"
services:
  db:
    #platform: linux/amd64 // Mac M1チップ以上の場合は必要
    image: mysql:5.7
    #container_name: "mysql57"
    volumes:
      - ./db/mysql:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root_pass_fB3uWvTS
      MYSQL_DATABASE: wordpress_db
      MYSQL_USER: user
      MYSQL_PASSWORD: user_pass_Ck6uTvrQ

  wordpress:
    image: wordpress:latest
    #container_name: "wordpress"
    volumes:
      - ./wordpress/html:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/conf.d/php.ini
    restart: always
    depends_on:
      - db
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: wordpress_db
      WORDPRESS_DB_USER: user
      WORDPRESS_DB_PASSWORD: user_pass_Ck6uTvrQ

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    #container_name: "phpmyadmin"
    restart: always
    depends_on:
      - db
    ports:
      - 8888:80
```
上記(2)で作成したファイル「docker-compose.yml」の内容となります。上記をコピー＆ペースト＆保存してください。
以上で、準備完了です。

## 4） docker-compose のアップ
```terminal:terminal
$ docker compose up -d
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283603/75cbc2ae-424f-f092-5bcd-65a54ffa4ede.png)
 「docker-compose.yml」 がある作業フォルダにおいて `docker compose up -d` すると、Dockerのコンテナが起動します。

## 5） WordPress の表示確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283603/5d688080-3735-424d-9265-f1577840b6eb.png)

ブラウザで `http://localhost:8080/` （もしくは`http://127.0.0.1:8080/`） へアクセスして WordPress のインストール画面が表示されれば成功です。

## 6) phpMyAdmin の表示確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283603/79fc4839-9c50-809d-bbb1-59d703feda87.png)

ブラウザで `http://localhost:8888/` へアクセスして phpMyAdmin のログイン画面が表示されれば成功です。
なお上記で「docker-compose.yml」ファイルをコピペしていた場合は
ユーザー名：`user`　／　パスワード：`user_pass_Ck6uTvrQ`
でログインできるはずです。

## 7） docker-compose のダウン
```terminal:terminal
$ docker-compose down
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283603/2ccbab47-e147-99cb-f8ef-d242c01e6e4d.png)
作業フォルダにおいて `docker-compose down` すると、サービスが終了し**コンテナが消えます**。

---

# 解説

```yaml:docker-compose.yml
version: "3" # おまじない（Composeファイル形式の宣言）
services: # サービス
  db: # (任意の文字列) サービスの名称：データベース
    #platform: linux/amd64 // Mac M1チップ以上の場合は必要
    image: mysql:5.7 # 用いるdockerイメージの指定
    #container_name: "mysql57" # (任意の文字列) コンテナの名称
    volumes: # 作業フォルダの同期（コンテナ側ディレクトリをホスト側へマウント）
      - ./db/mysql:/var/lib/mysql # ホストの作業フォルダ内「./db/mysql」とコンテナ内「/var/lib/mysql」を同期
    restart: always # おまじない（いつもコンテナの再起動を実施）
    environment: # 環境変数の指定
      MYSQL_ROOT_PASSWORD: root_pass_fB3uWvTS # (任意の文字列) mysqlのルートパスワード
      MYSQL_DATABASE: wordpress_db # (任意の文字列) mysql内のデータベース名 ※1
      MYSQL_USER: user # (任意の文字列) mysqlへログインする際のユーザー名 ※2
      MYSQL_PASSWORD: user_pass_Ck6uTvrQ # (任意の文字列) mysqlへログインする際のパスワード ※3

  wordpress: # (任意の文字列) サービスの名称：ワードプレス
    image: wordpress:latest # 用いるdockerイメージの指定
    #container_name: "wordpress" # (任意の文字列) コンテナの名称
    volumes: # 作業フォルダの同期（コンテナ側ディレクトリをホスト側へマウント）
      - ./wordpress/html:/var/www/html # ホストの作業フォルダ内「./wordpress/html」とコンテナ内「/var/www/html」を同期
      - ./php/php.ini:/usr/local/etc/php/conf.d/php.ini # ホストの作業フォルダ内「./php/php.ini」とコンテナ内「/usr/local/etc/php/conf.d/php.ini」を同期 ※注意！
    restart: always # おまじない（いつもコンテナの再起動を実施）
    depends_on: # サービス間の依存関係を指定
      - db # サービス「db」と関係がありますよ（先に「db」を起動してね。「wordpress」の起動には「db」の起動が必要よ）
    ports: # 公開用のポートの指定
      - "8080:80" # ホスト側のブラウザで「localhost:8080」にアクセスしたら、コンテナ側の「80ポート」につながり、WordPressが表示される
    environment: # 環境変数の指定
      WORDPRESS_DB_HOST: db:3306 # サービス「db」の3306ポートに接続してね
      WORDPRESS_DB_NAME: wordpress_db #  (任意の文字列) 使うデータベースは「wordpress_db」だよ ※1と合わせる
      WORDPRESS_DB_USER: user # (任意の文字列) mysqlへログインする際のユーザー名 ※2と合わせる
      WORDPRESS_DB_PASSWORD: user_pass_Ck6uTvrQ # (任意の文字列) mysqlへログインする際のパスワード ※3と合わせる

  phpmyadmin: # (任意の文字列) サービスの名称：phpmyadmin
    image: phpmyadmin/phpmyadmin:latest # 用いるdockerイメージの指定
    #container_name: "phpmyadmin" # (任意の文字列) コンテナの名称
    restart: always # おまじない（いつもコンテナの再起動を実施）
    depends_on: # サービス間の依存関係を指定
      - db # サービス「db」と関係がありますよ（先に「db」を起動してね。「phpmyadmin」の起動には「db」の起動が必要よ）
    ports: # 公開用のポートの指定
      - "8888:80" # ホスト側のブラウザで「localhost:8888」にアクセスしたら、コンテナ側の「80ポート」につながり、phpmyadminが表示される
```

## YAMLファイル
「docker-compose.yml」ファイルはYAML（ヤムル）記法で記述します。半角スペースのインデントが大事です。ズレたりしないように気をつけましょう。なお「docker-compose.<font color="#B0B">yaml</font>」でも（拡張子が「.yaml」でも）動きます。

## version:
おまじないのように記載しておきましょう。本当は `version: "3"` よりも `version: "3.1"` の方がいいよ（マイナーバージョンも記載しましょう）。`version: "3"` は `version: "3.0"` になるよ（最新バージョンやおまへんで）。みたいな事が[ココら](https://docs.docker.com/compose/compose-file/compose-versioning/#versioning)に書いています。

なお `version: "2"` と `version: "3"` は多少異なります。
例えば `version: "2"` であった `volumes_from：` は、`version: "3"` では除去されています（使えない）。
そんな事が[ココら](https://docs.docker.com/compose/compose-file/compose-versioning/#version-2x-to-3x#version-2x-to-3x)に書いています。

## services:
`services:` の下に使うサービスを記載していきます。
`services:` の同列に `volumes:` と `networks:` があります（今回は使わないので解説は省略）
なお `volumes:` と `networks:` は、 `services:` と**同列のもの**と、 `services:` に**属するもの**があります。

```yaml:docker-compose.yml
services: # サービス
  db:
    volumes: # サービスに属する volumes　（今回つかう）
    networks: # サービスに属する networks　（今回つかわない）

volumes: # サービスと同列にある volumes　（今回つかわない）
networks: # サービスと同列にある networks　（今回つかわない）
```

## サービスの名称
`db:` とか `wordpress:` とか `phpmyadmin:` ってなっている部分です。
サービスの名称は任意で（好きに）記載しても良いですが、他のサービスで参照することがあるので、その点は注意しましょう。

```yaml:docker-compose.yml
version: "3"
services:
  db: # ココで定義したサービス名を...
    # 〜省略〜
  wordpress:
    # 〜省略〜
    depends_on:
      - db      # ← ココで使っている
# 〜省略〜    
```

## image:
用いるDockerのイメージを指定します。
注意点は `image: mysql:5.7` の部分です。MySQLは `8.0` バージョンもありますが、こちらは何やら面倒なことになるようで、バージョンを `5.7` に指定する方が無難なようです。
参考サイト：[MySQL 8.0 で、WordPress に接続できなくなった](https://www.white-space.work/mysql8-connection-error-with-wordpress/)

## container_name:
コンテナの名称を指定する場合に用います。
私の使い方として、コンテナ名のコンフリクトでエラーが出るのが嫌なので、通常の私はコメントアウトしています。

## volumes:
※ここでの `volumes:` は、<font color="#b0b">**サービスに属する** `volumes:` のこと</font>です。
`$ docker-compose down` するとコンテナも消えてしまいます。つまり、コンテナを起動してからの変更内容が全部すっ飛びます。でも、当然に変更内容を残したい（永続化したい）ことがあります。そんなときに使うのが`volumes:`です。
詳細は[ココ](http://docs.docker.jp/v1.11/compose/compose-file.html#volumes-volume-driver)に記載があります。

```yaml:docker-compose.yml
version: "3"
services:
  db:
      # 〜省略〜
    volumes:
      - ./db/mysql:/var/lib/mysql
      # 〜省略〜
  wordpress:
      # 〜省略〜
    volumes:
      - ./wordpress/html:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/conf.d/php.ini
# 〜省略〜
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283603/48d521dc-97e3-41d5-a20f-ceb840da25b5.png)

### ホスト上のパス ： コンテナ上のパス

`ホスト上の同期したいディレクトリのパス ： コンテナ上の同期させたいディレクトリのパス` を記載すると、コンテナ上のディレクトリと、ホスト上のディレクトリ（作業フォルダ内）が同期されます。 `$ docker-compose down` で**コンテナが消えても、同期されたホスト上のディレクトリの内容は残ります**。

上記のサービス「WordPress」の場合だと、 `./wordpress/html` がホスト上のパス（ディレクトリ）、 `/var/www/html` がコンテナ上のパスです。
例えば、コンテナが起動中に、ホスト上のファイル`./wordpress/html/wp-config.php`を変更すると、コンテナ上のファイル`/var/www/html/wp-config.php`も変更されます。
何かの度に `$ docker exec`でコンテナへ入らなくてもいいので便利です。

同様に、ホスト上の作業フォルダへデータベースも残るように、サービス「db」では `./db/mysql` を指定しています。サービス「WordPress」の `./php/php.ini` はPHPの設定ファイル「php.ini」を同期させるために指定しています（WordPressのアッブロードサイズの制限を変更するなどの場合に「php.ini」を触ることがあります）。

## restart:
これもおまじない？ です。`restart: always` を入れときゃいいかと。
詳しく知りたいヒトは...[ホストを再起動した時などにDockerコンテナを自動的に再起動できるようにする](https://unskilled.site/ホストを再起動した時などにdockerコンテナを自動的/)

## depends_on:
サービス間の依存関係を指定します。
WordPressを動かすにはMySQLが必要です（データベースがなければ正常に動きません）。
phpMyAdminを動かすにもMySQLが必要です（だってphpMyAdminはMySQLを覗くためにあるのだから）。
MySQLはWordPressとphpMyAdminから依存されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283603/9fc69f7a-d1d5-ab24-c854-787844283a9b.png)

## ports:
公開用のポートを指定します。
`ホスト側ポート:コンテナ側ポート` で記載します。
**コンテナを起動中に**、ブラウザからURL `http://localhost` へアクセスしてWordPressを表示させたければ `"80:80"` と記載します。

私の場合はポートを指定したいことがあるので `"8080:80"` としています。するとURL `http://localhost:8080` でWordPressを表示させることができます。phpMyAdminは `"8888:80"` としているのでURL `http://localhost:8888` でアクセスできます。

```yaml:docker-compose.yml
# 〜省略〜

  wordpress:
    # 〜省略〜
    ports:
      - 8080:80 # ホスト側のブラウザで「localhost:8080」にアクセスしたら、コンテナ側の「80ポート」につながり、wordpressが表示される
    # 〜省略〜

  phpmyadmin:
    # 〜省略〜
    ports:
      - 8888:80 # ホスト側のブラウザで「localhost:8888」にアクセスしたら、コンテナ側の「80ポート」につながり、phpmyadminが表示される
```

### 【寄り道】 「nginx-proxy」でサブドメインでのアクセス
イメージ `jwilder/nginx-proxy` を使うと、任意のサブドメインでアクセスすることもできます（寄り道なので詳細を省きます）。

<details>
<summary><font color="#00b">サブドメインでアクセスの「docker-compose.yml」</font></summary>
（1）サービス「nginx-proxy」を加える（`image: jwilder/nginx-proxy`）。
（2）サブドメインでアクセスしたいサービスの`environment:`に`VIRTUAL_HOST:`を加える。
<pre>
version: "3"
services:
  db:
    image: mysql:5.7
    #container_name: "mysql57"
    volumes:
      - ./db/mysql:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root_pass_fB3uWvTS
      MYSQL_DATABASE: wordpress_db
      MYSQL_USER: user
      MYSQL_PASSWORD: user_pass_Ck6uTvrQ
  wordpress:
    image: wordpress:latest
    #container_name: "wordpress"
    volumes:
      - ./wordpress/html:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/conf.d/php.ini
    restart: always
    depends_on:
      - db
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: wordpress_db
      WORDPRESS_DB_USER: user
      WORDPRESS_DB_PASSWORD: user_pass_Ck6uTvrQ
      VIRTUAL_HOST: localhost # http://localhost へアクセスするとwordpressが表示
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    #container_name: "phpmyadmin"
    restart: always
    depends_on:
      - db
    environment:
      VIRTUAL_HOST: phpmyadmin.localhost # http://phpmyadmin.localhost へアクセスするとwordpressが表示
  nginx-proxy:
    image: jwilder/nginx-proxy
    # container_name: "nginx-proxy"
    ports:
      - 80:80
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
</pre>
</details>

## environment:
各サービスにおける環境変数を指定します。
どんな環境変数を記載するかは、用いるサービスによって異なります。
サービス「db」では、MySQLへのログインするための情報を記載し、サービス「WordPress」では、どのデータベースへ接続するかを記載しています。


「db」側のデータベース名・ユーザー名・パスワードと、「WordPress」側のデータベース名・ユーザー名・パスワードが一致するように注意しましょう。

```yaml:docker-compose.yml
version: "3"
services:
  db:
    # 〜省略〜
    environment: # 環境変数の指定
      MYSQL_ROOT_PASSWORD: root_pass_fB3uWvTS # (任意の文字列) mysqlのルートパスワード （設定は必須だが、普段使うことは無い？）
      MYSQL_DATABASE: wordpress_db # (任意の文字列) mysql内のデータベース名 ※1
      MYSQL_USER: user # (任意の文字列) mysqlへログインする際のユーザー名 ※2
      MYSQL_PASSWORD: user_pass_Ck6uTvrQ # (任意の文字列) mysqlへログインする際のパスワード ※3

  wordpress:
    # 〜省略〜
    environment: # 環境変数の指定
      WORDPRESS_DB_HOST: db:3306 # サービス「db」の3306ポートに接続してね
      WORDPRESS_DB_NAME: wordpress_db #  (任意の文字列) 使うデータベースは「wordpress_db」だよ ※1と合わせる
      WORDPRESS_DB_USER: user # (任意の文字列) mysqlへログインする際のユーザー名 ※2と合わせる
      WORDPRESS_DB_PASSWORD: user_pass_Ck6uTvrQ # (任意の文字列) mysqlへログインする際のパスワード ※3と合わせる
  # 〜省略〜
```

---

# うまくいかなかったとき
うまくいかない理由は様々あると思いますが、私はこれで手間取りました。

## volumes:php.ini

```yaml:docker-compose.yml
# 〜省略〜
wordpress:
    image: wordpress:latest
    #container_name: "wordpress"
    volumes:
      - ./wordpress/html:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/conf.d/php.ini # ← コイツ
# 〜省略〜
```
上記の「コイツ」の部分を削除して試してみてください。削除してうまくいったら、コイツが犯人です。
コイツの部分を `./php:/usr/local/etc/php/conf.d` とか `./php:/usr/local/etc/php` ってすると、**うまくいきません**。

### 上手く行かなかったワケ
下記のサイトが参考になりました。
[Docker でボリュームをマウントする際はホスト側ファイルがコンテナ側ファイルを上書きする](https://obel.hatenablog.jp/entry/20170805/1501900413)
WordPressを起動するのに必要なファイルがつまったコンテナ側の `/usr/local/etc/php` を、何も入っていないホスト側のディレクトリ `./php` で、上書き（消し去って）してしまったことが原因でした。


### php.iniを設定したいとき
**php.iniを用意せず**に `volumes:./php/php.ini` すると、「php.ini」がディレクトリとして同期されます（作業フォルダに「php.ini」というフォルダが生成されます）。
php.iniを設定したいときは、**作業フォルダの「php.ini」フォルダを消して、ファイルの「php.ini」を入れてください**（要は入れ替えてください）。その後にコンテナを再構築してください。
（`$ docker-compose up -d` →　`$ docker-compose down` → 「php.ini」を入れ変える → `$ docker-compose up -d`。）

---

# リスペクトな記事
本文中の参考サイトに加え、本記事のために参考・勉強させていただきました記事です
[docker-compose コマンドまとめ](https://qiita.com/wasanx25/items/d47caf37b79e855af95f)
[初心者向けdocker-composeコマンド逆引き](https://qiita.com/okyk/items/a374ddb3f853d1688820)
[docker-compose up とか build とか start とかの違いを理解できていなかったのでまとめてみた。](https://qiita.com/tegnike/items/bcdcee0320e11a928d46)
[Docker Volume (特に volumeタイプ) のわかりづらいところを説明してみる](https://qiita.com/mtgto/items/aabc212a1d3f99878828)
[Docker、ボリューム(Volume)について真面目に調べた](https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32)
[HTTPSに対応したWordPressのローカル開発環境をDockerで構築](https://qiita.com/matsuri0828/items/b4259e385996daa8aa6e)
[HTTPSなWordPressをDocker + Let's Encryptで速攻デプロイ](https://qiita.com/devoimo/items/8a59d658f4be2e842c1e)