Docker+Laravel+PostgreSQL+pgAdmin4+Apacheの開発環境を構築する

#目的
これまで、Dockerで構築したLaravel+PostgreSQLの開発環境に、必要に応じて様々なパッケージなどを追加してきました。ここらでDockerfileなどの中身を整理して、備忘録として残したいと思います。

Gitからサンプルプロジェクトをクローンしていただければ、誰でも簡単にLaravel+PostgreSQLの環境を構築することができると思います。みなさんの好みに合わせて、カスタマイズしながら使っていただければ幸いです。

#環境
- Windows 10 Pro 64bit メモリ 32GB
- Docker for Windows v20.10.2
- Docker Compose v1.27.4
- Git 

DockerやGitをこれからインストールする方は、下記の記事が参考になると思います。

-  [[Docker] Windows 10 Pro 64bit に Docker と Docker Compose をインストールする](https://qiita.com/ksh-fthr/items/6b1242c010fac7395a45)
-  [Windows10にGitをインストール (2021年02月更新)](https://www.curict.com/item/60/60bfe0e.html)

#用意する開発環境
- PHP 7.2
- Laravel 最新バージョン
- PostgreSQL 10.8
- pgAdmin4 4.2
- Apache 2.4.38

#サンプルプロジェクトの取得

後述するファイル構成のサンプルプロジェクトを用意しました。
クローンしていただき、ご自由にお使いください。

```
[win] $ git clone https://github.com/sakeafterbeer/dev-docker-laravel-postgresql.git
```

#ファイル構成
```
[win] $ tree
.
┣ docker/
┃    ┣ docker-entrypoint-initdb.d/ // PostgreSQLのDBセットアップ用ディレクトリ
┃    ┃
┃    ┣ pgadmin4/ // pgAdmin4のデータ格納用ディレクトリ
┃    ┃
┃    ┣ Dockerfile
┃    ┃
┃    ┗ php.ini // PHP設定ファイル
┃ 
┣ prj/ // ソースコード格納用ディレクトリ
┃
┗ docker-compose.yml
```

docker-entrypoint-initdb.dに「.sql」「.sh」「.sql.gz」などのファイルを置いておくと、PostgreSQLイメージからコンテナを生成・起動する際に実行してくれます。

pg_dumpallやpg_dumpで出力したスクリプトも、拡張子が「.sql」であれば実行可能です。pg_dumpallについても記事を書いたので、ご参考になれば幸いです。

- [PostgreSQLのデータをpg_dumpallで移行する](https://qiita.com/sakeafterbeer/items/babda2ca964e5884ea5b)

###docker-entrypoint-initdb.dに置いたファイルが実行されない場合

docker-entrypoint-initdb.dに置かれたファイルは、コンテナの初回の生成・起動時のみに実行されるようです。そのため、何度もコンテナを作り直したりしているとファイルが実行されなくなってしまいます。

この様な場合には、以下のコマンドを実行してコンテナ・ネットワーク・ボリューム・イメージを削除し、改めてコンテナを生成・起動します。

```
[win] $ docker-compose down -v
[win] $ docker-compose build --no-cache
[win] $ docker-compose up -d
```

[docker-compose down -v](https://docs.docker.jp/compose/reference/down.html): コンテナ・ネットワーク・ボリューム・イメージを削除します。
[docker-compose build --no-cache](https://docs.docker.jp/compose/reference/build.html): Dockerイメージをキャッシュを使わずに作成します。
[docker-compose up -d](https://docs.docker.jp/compose/reference/up.html): コンテナの構築、作成、バックグラウンドでの起動、アタッチを行います。

#Dockerfile
```.dockerfile:Dockerfile
# 1. Set the base image.
FROM php:7.2-apache

# 2. Install Xdebug.
RUN pecl install xdebug \
    && docker-php-ext-enable xdebug

# 3. Change the Apache document root.
ENV APACHE_DOCUMENT_ROOT /var/www/html/public

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

#4. Install packages, extensions and composer.
RUN apt-get update && apt-get install -y \
    vim \
    git \
    unzip \
    zlib1g-dev \
    && docker-php-ext-install \
    sockets \
    zip \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
RUN curl -s http://getcomposer.org/installer | php && \
    echo "export PATH=${PATH}:/var/www/vendor/bin" >> ~/.bashrc && \
    mv composer.phar /usr/local/bin/composer

#5. Install the GD library.
RUN apt-get update && apt-get install -y \
    gcc \
    libfreetype6-dev \
    libjpeg-dev \
    libmcrypt-dev \
    libpng-dev \
    make \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd

# 6. Make "npm install" executable on the container for using Vue.js.
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \
    && apt-get update && apt-get install -y \
    nodejs

# 7. Enable mod_rewrite.
RUN a2enmod rewrite

#8. Make postgreSQL accessible from the container.
RUN set -ex apk --no-cache add postgresql-dev libpq-dev && \
    apt-get update && apt-get install -y \
    less \
    libpq-dev \
    postgresql \
    && docker-php-ext-install \
    mbstring \
    pdo \
    pdo_pgsql \
    pgsql
```
#Dockerfileの解説

###Dockerコマンドについて

[FROM](https://docs.docker.jp/engine/reference/builder.html#from): ベース・イメージを設定します。
[ENV](https://docs.docker.jp/engine/reference/builder.html#env): 環境変数を設定します。
[RUN](https://docs.docker.jp/engine/reference/builder.html#run): コマンドを実行します。 

###Dockerfileの処理内容について

1. [php:7.2-apache](https://hub.docker.com/layers/php/library/php/7.2-apache/images/sha256-77e5a326252f951aa557f48829973f67e8efde9c52f81ee4e4a5473a59a217d9?context=explore)をベース・イメージとして設定します。
2. [Xdebug](https://liginc.co.jp/224022)をインストールします。
3. Apacheのドキュメントルートを変更します。
4. 開発環境で利用するパッケージやエクステンション、コンポーザーをインストールします。
5. GDライブラリをインストールします。
6. Vue.jsを利用するため、Node.jsをインストールします。
7. mod_rewriteモジュールを有効にします。
8. コンテナからpostgreSQLにアクセスできるようにします。

###注意事項
Dockerfileの書き方がベストプラクティスに基づいていないので、徐々に改修していく予定です。改善すべき点は多々あると思いますので、ご指摘いただければ幸いです。

#docker-compose.yml
```YAML:docker-compose.yml
version: '3'

volumes:
  db-data:

services:
  php-cli: # Web server for remote debug on PhpStorm.
    build: ./docker
    volumes:
      - ./prj:/var/www/html/
      - ./docker/php.ini:/usr/local/etc/php/php.ini
    working_dir: /var/www/html
    container_name: php-cli
    depends_on:
      - postgres
    restart: always

  web:
    build: ./docker
    ports:
      - 80:80
    volumes:
      - ./prj:/var/www/html/
      - ./docker/php.ini:/usr/local/etc/php/php.ini
    working_dir: /var/www/html
    container_name: web
    depends_on:
      - postgres
    restart: always
  
  postgres:
    image: postgres:10.8
    ports:
      - 5432:5432
    volumes:
      - db-data:/var/lib/postgresql/data
      - ./docker/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
    environment: # Set the postgres user name, password and database name.
      POSTGRES_USER: default
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: postgres
    hostname: postgres
    container_name: postgres
    restart: always

  pgadmin4:
    image: dpage/pgadmin4:4.2
    ports:
        - 8000:80
    volumes:
        - ./docker/pgadmin4:/var/lib/pgadmin
    environment: # Set the login information of pgAdmin 4.
        PGADMIN_DEFAULT_EMAIL: root
        PGADMIN_DEFAULT_PASSWORD: root
    hostname: pgadmin4
    container_name: pgadmin4
    depends_on:
      - postgres
    restart: always
```

#docker-compose.ymlの解説

Docker Composeでは、docker-compose.ymlという名前のYAMLファイルと使って複数のコンテナを定義し実行します。上記のdocker-compose.ymlでは以下の4つのコンテナの設定を行っています。

php-cli: PhpStormからリモートデバッグでPHPUnitを実行するためのコンテナです。
web: Laravelを実行するためのコンテナです。
postgres: PostgreSQLを実行するためのコンテナです。
pgadmin4: pgAdmin4を実行するためのコンテナです。

#PHP設定ファイル
```php.ini
; Date
date.timezone = Asia/Tokyo

; Core
log_errors = On
error_log = /dev/stderr
display_errors = On
error_reporting = E_ALL

; Xdebug
xdebug.remote_enable = On
xdebug.remote_autostart = On
xdebug.remote_connect_back = Off
xdebug.remote_host = host.docker.internal
xdebug.remote_port=9000
xdebug.idekey=phpstorm
xdebug.remote_log="/tmp/xdebug.log"

; mbstring
mbstring.language = Japanese
mbstring.internal_encoding = auto
mbstring.http_input = auto
mbstring.http_output = auto
mbsting.encoding_translation = Off
mbstring.detect_order = auto
```

#コンテナのビルドからLaravelを動かすまで

###コンテナを生成・起動する

dev-docker-laravel-postgresqlディレクトリに移動して、コンテナを生成・起動します。
データベースの初期化スクリプトがあれば、docker-entrypoint-initdb.dディレクトリに格納しておいてください。

```
[win] $ cd dev-docker-laravel-postgresql
[win] $ docker-compose up -d
```

コンテナ一覧を表示すると、コンテナが生成・起動されていることがわかります。

```
[win] $ docker-compose ps
  Name                Command               State               Ports
---------------------------------------------------------------------------------
pgadmin4   /entrypoint.sh                   Up      443/tcp, 0.0.0.0:8000->80/tcp
php-cli    docker-php-entrypoint apac ...   Up      80/tcp
postgres   docker-entrypoint.sh postgres    Up      0.0.0.0:5432->5432/tcp
web        docker-php-entrypoint apac ...   Up      0.0.0.0:80->80/tcp
```

###コンテナの中に入る

Laravelプロジェクトを作成するため、webコンテナに入ります。

```
[win] $ docker-compose exec web bash
```

###composerコマンドでLaravelプロジェクトを作成する

Laravelプロジェクトを「sample」というプロジェクトフォルダ名で作成し、storage/とbootstrap/cache/ディレクトリのパーミションを変更して、コンテナから出ています。

```
[web] $ composer create-project --prefer-dist laravel/laravel sample
[web] $ cd sample
[web] $ chmod 777 -R bootstrap/cache/
[web] $ chmod 777 -R storage/
[web] $ exit
```

###docker-compose.ymlのプロジェクトフォルダのパスを書き換える

prjディレクトリの中にsampleというプロジェクトが作成せれているので、docker-compose.ymlのボリュームのパスを書き換えます。
※php-cliとwebの両方を書き換えてください。

```YAML:docker-compose.yml
    volumes:
      - ./prj/sample:/var/www/html
```

###docker-compose.ymlの設定を反映させるため、コンテナを再起動する

```
[win] $ docker-compose up -d php-cli web
```

再起動すると__/prj/sample__が__/var/www/html__にマウントされて、Laravelを表示できるようになります。

###Laravelを表示する

localhostに接続して、Laravelを表示してみます。

http://localhost/

![laravel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/35b7848e-93e7-d64e-cd50-3c0abf5f19ec.png)

無事に表示されました！


#データベースに接続する

webコンテナからpsqlコマンドでデータベースに接続してみます。

```
[win] $ psql -h postgres -U default -d postgres
```

docker-compose.ymlで指定した値をオプションの引数に指定しています。

-h: ホスト名
-U: ユーザー名
-d: データベース名

パスワードもdocker-compose.ymlに設定した「secret」を使います。

```
Password for user default:
psql (11.10 (Debian 11.10-0+deb10u1), server 10.8 (Debian 10.8-1.pgdg90+1))
Type "help" for help.

postgres=#
```

無事に接続できました！これでSQLコマンドでデータベースを操作することができます。

#pgAdmin4を表示する

最後にlocalhost:8000に接続して、pgAdmin4を表示してみます。

http://localhost:8000/

![pgadmin4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/34f2d980-3e39-7b1a-6aef-e3d15ddd8bf1.png)

無事に表示できました！

docker-compose.ymlで設定したメール(root)とパスワード(root)でログインしてみます。

![pgadmin4_logged_in.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/ffa4892b-366f-8fb1-d5ca-2be3161f619c.png)

無事にログインできました！

ただし、このままではpostgresの内容を表示することはできません。
サーバを作成して、Postgresと接続する設定をする必要があります。

###サーバーを作成する

Server > 作成 > サーバ... を選択します。

![pgadmin1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/86ee947b-8253-f9fe-5f59-81551e1a1a4d.png)

「一般」の設定にて、サーバに任意の名称をつけます。

![pgadmin2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/e7f1ac9e-98f6-49e5-13de-5e2f62f9bb94.png)

「接続」の設定にて、postgresコンテナのホスト名やポート番号などを入力します。
※docker-compose.ymlで設定した項目です。

![pgadmin3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/fd5c8c2f-34fb-d3f2-93ce-519812052815.png)

入力したら「保存」をクリックします。

![pgadmin4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/8c50feaa-bce9-b471-73ff-8e163c623c62.png)

これで設定完了です！お疲れさまでした！

#おわりに

間違っている点やもっと良い方法などありましたら、ご指摘いただけるとありがたいです！
最後までお読みいただきありがとうございました！

#参考にした記事

- [Docker-docs-ja Docker ドキュメント日本語化プロジェクト](https://docs.docker.jp/index.html#)
- [Docker-docs-ja Docker（Docker Compose コマンドライン・リファレンス）](https://docs.docker.jp/compose/reference/down.html)
- [FROM:latest（DockerfileのLintサイトです）](https://www.fromlatest.io/#/)
- [本番運用に備えたPHP7.3.0のコンテナを作る](https://qiita.com/tomoyamachi/items/ff5ac4a96063a12409cd)
- [【PHP】Docker PHP7.4系でgdをインストールしてimagecreatefromjpegを使う](https://uiuifree.com/blog/develop/docker-gd-php-7/)
- [UbuntuにてApacheのモジュールを有効化する](https://www.t3a.jp/blog/infrastructure/apache2-mod-add/)
- [[備忘録]Dockerでapache+php+postgresql環境](https://qiita.com/cyclon2joker/items/39e620d3d16fa1f6edf0)
- [PhpStorm + Docker for Mac（docker-compose）での PHPUnit と Remote Debug の設定](https://blog.shin1x1.com/entry/setup-test-and-debug-on-phpstorm-and-docker-for-mac)
- [[PHP] Xdebug のリモートデバッグ、理解していますか？](https://qiita.com/castaneai/items/d5fdf577a348012ed8af)
- [Docker-docs-ja Docker Compose 概要](https://docs.docker.jp/compose/index.html)
- [NestJS+PostgreSQL(+pgAdmin4)の開発環境をDockerで構築する](https://zenn.dev/kobayashiyabako/articles/3a6c575aafae67a41d25)