Dockerで複数Laravel5.5プロジェクトの環境構築(1)

## 概要

今回はdockerを用いてphp7の動作環境を構築していく。ローカル開発環境を構築するのに便利なdockerを用いる。vagrant+chef|ansibleに比べて開発環境のメンテナンスが非常に楽でオススメです。

本記事は(1)と(2)の２つに分ける予定です。
(1)ではdockerで(php+apache)×2+mysql+composer+yarnのコンテナを構築し、nginxリバースプロキシ経由でそれぞれのアプリケーションに別々のドメインでアクセスまでを解説します。
(2)では実際にLaravelのプロジェクトを構築し、必要な操作を解説する予定です。

追記) [Dockerで複数Laravel5.5プロジェクトの環境構築(2)](https://qiita.com/zizu21105/items/ecb865c6ab61376c6426)

## この記事で触れること

- docker-composeを使ってlaravelに必要な環境を構築
- ローカル環境で複数ドメインへのアクセスをリバースプロキシで振り分け

## この記事で触れないこと
- Laravelアプリケーションの開発手順
- dockerのインストール手順
- dockerの詳細
- 本番・STG環境へのデプロイ

## 開発環境
- Ubuntu GNOME 16.04
- Docker 17.12.0-ce

## プロジェクト構成
[github リポジトリ](https://github.com/zizu21105/laravel-docker)

```bash
$ tree                                                                                                                                                  
.
├── composer
│   └── Dockerfile
├── data
│   ├── foo
│   │   └── public
│   │       └── index.html
│   └── hoge
│       └── public
├── docker-compose.yml
├── LICENSE
├── mysql
│   └── Dockerfile
├── php-common
│   ├── Dockerfile
│   ├── foo.conf
│   ├── hoge.conf
│   └── php.ini
├── proxy
│   ├── certs
│   │   ├── server.crt
│   │   ├── server.csr
│   │   └── server.key
│   ├── Dockerfile
│   └── nginx.conf
├── README.md
└── yarn
    └── Dockerfile
```

## 手順
まずはプロジェクトディレクトリを用意。今回はlaravel-dockerとします。
とりあえず構築を目的とする方は、上に貼っておいたgithubのリポジトリを参照してください。

手順を確認したい方は以下の順番で構築してください。
### 1. docker-compose.ymlの作成
複数のdockerコンテナを一括管理するため、docker-composeを用います。

```docker-compose.yml
version: '3.4'
services:
  proxy:
    build: ./proxy
    container_name: proxy
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - type: bind
        source: ./proxy/nginx.conf
        target: /etc/nginx/conf.d/nginx.conf
      - type: bind
        source: ./proxy/certs
        target: /etc/nginx/certs
      - /var/run/docker.sock:/tmp/docker.sock:ro

  hoge:
    build: ./php-common
    container_name: php-hoge
    working_dir: /target/path
    environment:
      - VIRTUAL_HOST=hoge.hoge.com
    volumes:
      - type: bind
        source: ./data/hoge
        target: /var/www/html/hoge
      - type: bind
        source: ./php-common/hoge.conf
        target: /etc/apache2/apache2.conf
      - type: bind
        source: ./php-common/php.ini
        target: /usr/local/etc/php/php.ini

  foo:
    build: ./php-common
    container_name: php-foo
    working_dir: /target/path
    environment:
      - VIRTUAL_HOST=foo.foo.com
    volumes:
      - type: bind
        source: ./data/foo
        target: /var/www/html/foo
      - type: bind
        source: ./php-common/foo.conf
        target: /etc/apache2/apache2.conf
      - type: bind
        source: ./php-common/php.ini
        target: /usr/local/etc/php/php.ini

  hoge_composer:
    build: ./composer
    container_name: hoge_composer
    working_dir: /data/hoge
    restart: 'no'
    volumes:
      - type: bind
        source: ./data/hoge
        target: /data/hoge

  foo_composer:
    build: ./composer
    container_name: foo_composer
    working_dir: /data/foo
    restart: 'no'
    volumes:
      - type: bind
        source: ./data/foo
        target: /data/foo

  hoge_yarn:
    build: ./yarn
    container_name: hoge_yarn
    working_dir: /data/hoge
    restart: 'no'
    volumes:
      - type: bind
        source: ./data/hoge
        target: /data/hoge

  foo_yarn:
    build: ./yarn
    container_name: foo_yarn
    working_dir: /data/foo
    restart: 'no'
    volumes:
      - type: bind
        source: ./data/foo
        target: /data/foo

  mysql:
    build: ./mysql
    container_name: mysql
    volumes:
      - dbdata:/var/lib/mysql
    environment:
      - "MYSQL_DATABASE=homestead"
      - "MYSQL_USER=homestead"
      - "MYSQL_PASSWORD=root"
      - "MYSQL_ROOT_PASSWORD=root"
    ports:
      - "3306:3306"

volumes:
  dbdata:
```

次に、各コンテナ用のDockerfileを各ディレクトリ以下に用意

### 2. php
+ phpコンテナはapacheモジュールとして動作する公式イメージを利用し、必要なものをDockerfileに追記していきます。また、２つのlaravelプロジェクトで共通のコンテナを用います。

```dockerfile:laravel-docker/php-common/Dockerfile
FROM php:7.2.1-apache-stretch

RUN a2enmod rewrite

RUN apt-get update \
    && apt-get install -y zlib1g-dev \
    && docker-php-ext-install pdo_mysql mysqli mbstring \
    && apt-get install -y vim
```

```ini:laravel-docker/php-common/php.ini
略) 適当なphp.iniを参照。または、添付のgithubリポジトリからコピー。
```

- Virtualhostを設定

```conf:hoge.conf
<Virtualhost *:80>
  DocumentRoot /var/www/html/hoge/public
  <Directory /var/www/html/hoge/public>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    allow from all
  </Directory>
</Virtualhost>

Mutex file:${APACHE_LOCK_DIR} default
PidFile ${APACHE_PID_FILE}
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
HostnameLookups Off
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
Include ports.conf

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Require all denied
</Directory>

<Directory /usr/share>
    AllowOverride None
    Require all denied
</Directory>

<Directory /var/www>
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

AccessFileName .htaccess

<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>

IncludeOptional conf-enabled/*.conf
IncludeOptional sites-enabled/*.conf
```

```conf:foo.conf
<Virtualhost *:80>
  DocumentRoot /var/www/html/foo/public
  <Directory /var/www/html/foo/public>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    allow from all
  </Directory>
</Virtualhost>

Mutex file:${APACHE_LOCK_DIR} default
PidFile ${APACHE_PID_FILE}
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
HostnameLookups Off
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
Include ports.conf

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Require all denied
</Directory>

<Directory /usr/share>
    AllowOverride None
    Require all denied
</Directory>

<Directory /var/www>
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

AccessFileName .htaccess

<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>

IncludeOptional conf-enabled/*.conf
IncludeOptional sites-enabled/*.conf
```

### 3. Composer
+ composerは必要な際にコンテナ起動を行うため軽量なalpine製

```dockerfile:laravel-docker/composer/Dockerfile
FROM composer/composer:alpine

RUN composer self-update

ENTRYPOINT [ "composer" ]
CMD [ "--help" ]
```
### 4. MySQL
+ mysqlは公式のDockerイメージをそのまま利用

```dockerfile:laravel-docker/mysql/Dockerfile
FROM mysql:5.7.20
```
### 5. yarn
+ Laravelでwebpackなどnode_modulesを管理するためnpmが必要ですが、今回はFacebook製のyarnで代用

```dockerfile:laravel-docker/yarn/Dockerfile
FROM node:9.5-alpine
```
### 6. Proxy
+ リバースプロキシをnginxで構築。こちらも便利なイメージがすでに存在しましたので、そちらを元にしました。

```dockerfile:laravel-docker/proxy/Dockerfile
FROM jwilder/nginx-proxy
```

```nginx:laravel-docker/proxy/nginx.conf
server {
    listen 80;
    server_name hoge;

    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    server_name hoge.hoge.com;

    ssl                 on;
    ssl_certificate     /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;

    location / {
        proxy_pass http://php-hoge:80;
        proxy_redirect http:// https://;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

server {
    listen 80;
    server_name foo;

    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    server_name foo.foo.com;

    ssl                 on;
    ssl_certificate     /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;

    location / {
        proxy_pass http://php-foo:80;
        proxy_redirect http:// https://;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
```

+ オレオレ証明書を作成。リバース  
参照) [nginx ssl設定(オレオレ証明書)](https://qiita.com/forestkinoko/items/9d77dcbda20ee0706777)

```bash
# 秘密鍵の作成
openssl genrsa 2048 > server.key

# 証明書署名要求の作成
openssl req -new -key server.key > server.csr

# 自分で署名して100年間有効な証明書作る
openssl x509 -days 36500 -req -signkey server.key < server.csr > server.crt
```

### 7. ローカルマシンにデータ用ディレクトリを作成
```bash
$ pwd
# .../laravel-docker

$ mkdir data
```

## 起動
**オススメ**
docker-composeコマンドは長いのでdcなどのエイリアスを作成しておくと便利です。 

+ 作成したDockerfileからイメージをビルド

```bash
$ docker-compose build
```

+ ビルドに成功したら、コンテナを起動

```bash
$ docker-compose ps
# Name   Command   State   Ports
# ------------------------------

$ docker-compose up -d

$ docker-compose ps
#     Name                   Command               State                     Ports                  
# --------------------------------------------------------------------------------------------------
# foo_composer    composer --help                  Exit 0                                           
# foo_yarn        node                             Exit 0                                           
# hoge_composer   composer --help                  Exit 0                                           
# hoge_yarn       node                             Exit 0                                           
# mysql           docker-entrypoint.sh mysqld      Up       0.0.0.0:3306->3306/tcp                  
# php-foo         docker-php-entrypoint apac ...   Up       80/tcp                                  
# php-hoge        docker-php-entrypoint apac ...   Up       80/tcp                                  
# proxy           /app/docker-entrypoint.sh  ...   Up       0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

```

+ ローカルマシンのホストファイルにドメイン名を記述し、ブラウザからアクセスした際、ローカルにアクセスするように設定を追記

```bash
$ vi /etc/hosts

127.0.0.1 hoge.hoge.com
127.0.0.1 foo.foo.com
```
+ index.htmlファイルをドキュメントルートに配置

```html:laravel-docker/data/hoge/public/index.html
<html>
<h1>Hoge</h1>
</html>
```

## ブラウザでアクセス
https://hoge.hoge.com へアクセスし、以下の画面が表示されれば成功です。
警告が表示された場合、「例外に追加」などのオプションを選択して先に進むと表示されます。
![browser.png](https://qiita-image-store.s3.amazonaws.com/0/236140/e6f5cf76-9e27-dde7-5a2f-ec31bdddeee9.png)