DockerでLAMP + SSL +sSMTP+ node.js+gulp環境構築メモ（docker-syncも）


## 概要

Qiita初投稿です。

＊こちらの方法は古いので今使っている方法でのノートを近く公開する予定です。

WEBサイトの開発環境を簡単に用意できるようなテンプレートを作成した時の手順メモです。
年末にDocker-compose upで開発環境作れたらとても楽なのではとやり始めたのですが、DockerやLinuxの知識不足で手こずりました。
開発は主にMacで行なっているのでMac向けの設定で書いています。

SSLの設定は、自己認証局を一から作るととても面倒なので、「mkcert」を使うことにしました。
https://github.com/FiloSottile/mkcert

メール送信テストも行いたいですね。
Postfixとか設定大変です。
なのでこれも便利な「mailCatcher」を使います。
https://mailcatcher.me/

便利な時代になりましたね。

node.jsに関して。
最初、PHPのコンテナとNodeのコンテナを分けて構築する方法を考えていましたが、browser-sync関連で上手く行きませんでした。
仕方がないので　PHPのコンテナにnode.jsをインストールする方法で無理やり解決させました。
（開発環境なのでこれでよしとしました）
また、Debianのatp-getでインストール出来るnode.jsは更新が滞っていて古かったりします。
nodebrew経由でのインストールでは最新も使えるそうなので、nodebrewをコンテナ内にインストールすることで解決させました。

参考）
https://shioleap.github.io/blog/Linux/Debian/install-nodejs-on-nodebrew/

[追記2019/01/22 : なんかgulp.watchが遅いから調べた](https://qiita.com/soundweaver/items/615f2cac37d387661dc2#%E8%BF%BD%E8%A8%98--%E6%80%9D%E3%81%A3%E3%81%9F%E3%82%89%E3%81%AA%E3%82%93%E3%81%8Bgulpwatch%E3%81%8C%E9%81%85%E3%81%84%E3%81%AE%E3%81%A7%E8%AA%BF%E3%81%B9%E3%81%9F)

[さらに追記 20190123 : docker-syncで高速になって幸せに。](https://qiita.com/soundweaver/items/615f2cac37d387661dc2#docker-sync%E3%81%A7%E9%AB%98%E9%80%9F%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%A6%E5%B9%B8%E3%81%9B%E3%81%AB---docker-sync%E3%81%AE%E8%A8%AD%E5%AE%9A) 


## 構成

```tex
root
└・dataディレクトリ
└・dockerディレクトリ
  └・web
   ├・Dockerfile
   ├・php.ini
   ├・ssl.conf
   └.ssmp.conf
└・public_html
  └・src
   └・scss
  ├・index.html
  ├・npm_nodule_list.txt
  └・npm.sh
├・.env
└・docker-compose.yml
```

## 自己認証局が作れるmkcertを使うための下準備
https://github.com/FiloSottile/mkcert
Homebrewを使ってインストールしていきます。
ちなみにWindowsではChocolateyでインストールが出来るそうです。

### mkcertのインストール

```bash:Terminal
#Homebrewのインストール
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

#mkcertのインストール
$ brew install mkcert
```

### 自己認証局の設置

```bash:Terminal
#自己認証局のインストール
$ mkcert -install
```

### 開発用ドメインの登録

WEBサーバービルド用のDockerfileと同じディレクトリに移動してから実行すします。
今回は、localhostと開発マシンのローカルIPでの設定例にします。
例)

```bash:Terminal
$ cd ./docker/web/

$ mkcert localhost 192.168.XXX.1XXX

```

そうするとlocalhost+1-key.pemとlocalhost+1.pemとキーペアが作られます。

ちなみにmkcertは、複数のドメインやワイルドカードの設定なども出来るようです。

```bash:Terminal
#ドメイン固定の場合
$ mkcert local.hogehoge.dev
#ワイルドカードで全部のsub含める場合
$ mkcert *.hogehoge.dev
#自マシンのIPアドレスを使う場合
$ mkcert 192.168.3.100
#複数いっぺんにも可能
$ mkcert local.hogehoge.dev 192.168.3.100
```


## Dockerfileの作成（WEBサーバー用）

公式のApache＋PHPイーメージを使って、そこに必要なモジュールをインストールしていく方法にしました。
webディレクトリ内のDockerfileに読み込むイメージを指定します。

```dockerfile:Dockerfile
FROM php:7.2-apache
```

### PHPのセッティング＋α

7.2-apacheのイメージではOSがDebianになる様子です。
以下インストールなどはDebianのやり方で記載していきます。

まずは、WEBサイトで使うであろうPHPの各モジュールのインストールと設定をします。
ついでにここで、ssmptとvimとcurlをインストールする設定も記述していきます。

PHP辺りの設定は、docker公式のWordpressのイメージなどを参考に追加しています。
案件ごとに本番サーバーの設定が変わると思われるので、モジュールは本番環境と同じにするとよいと思います。

```dockerfile:Dockerfile
# install the PHP extensions we need
RUN set -ex; \
	\
	savedAptMark="$(apt-mark showmanual)"; \
	\
	apt-get update; \
	apt-get install -y --no-install-recommends \
		libwebp-dev \
    	libjpeg62-turbo-dev \
    	libpng-dev libxpm-dev \
    	libfreetype6-dev \
	; \
	#mail送信のみのパッケージのインストール
	apt-get install -y ssmtp mailutils; \
	#vimのインストール
	apt-get install -y vim; \
	#curlのインストール
	apt-get install curl; \
	#php gdの設定
	\
	docker-php-ext-configure gd \
	--with-webp-dir \
    --with-jpeg-dir \
    --with-png-dir \
    --with-zlib-dir \
    --with-xpm-dir \
    --with-freetype-dir \
    --enable-gd-native-ttf \
	--with-png-dir=/usr \
	--with-jpeg-dir=/usr; \
	docker-php-ext-install gd mysqli opcache zip; \
	\
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
	apt-mark auto '.*' > /dev/null; \
	apt-mark manual $savedAptMark; \
	ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \
		| awk '/=>/ { print $3 }' \
		| sort -u \
		| xargs -r dpkg-query -S \
		| cut -d: -f1 \
		| sort -u \
		| xargs -rt apt-mark manual; \
	\
	apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
	rm -rf /var/lib/apt/lists/*

# set recommended PHP.ini settings
# see https://secure.php.net/manual/en/opcache.installation.php
RUN { \
		echo 'opcache.memory_consumption=128'; \
		echo 'opcache.interned_strings_buffer=8'; \
		echo 'opcache.max_accelerated_files=4000'; \
		echo 'opcache.revalidate_freq=2'; \
		echo 'opcache.fast_shutdown=1'; \
		echo 'opcache.enable_cli=1'; \
	} > /usr/local/etc/php/conf.d/opcache-recommended.ini

RUN a2enmod rewrite expires
```

### ドキュメントルートの変更設定

本番環境によってはドキュメントルートが変わります。
デフォルトの/var/www/htmlのままでもいいのですが、合わせておくと何かと手間が省けるケースもあるので、ドキュメントルートを変更する設定も追加します。

```dockerfile:Dockerfile
#change document root
#ドキュメントルートを変更する設定
#ここは任意のディレクトリを指定します（本番環境に合わせてもOK）
#docker-compose.ymlとssl.confにもDocument Rootを設定する箇所あり
ENV APACHE_DOCUMENT_ROOT /home/XXXX/public_html/www

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

VOLUME APACHE_DOCUMENT_ROOT
```

### sSMTPの設定

sendmail pathをssmtpで使えるようにphp.iniにて設定

```ini:php.ini

[Date]
date.timezone = "Asia/Tokyo"

[Mail]
sendmail_path = "/usr/sbin/ssmtp -t"
```

mailcatcherがstmpのポート1025を使うので、ssmp.confにて設定

```config:ssmp.conf

#
# Config file for sSMTP sendmail
#
mailhub=smtp:1025
FromLineOverride=YES
```

### SSLの設定

ssl.confの設定

```conf:ssl.conf
#SSLのキーペアを設定する
SSLCertificateFile  /etc/apache2/ssl_keys/localhost+1.pem
SSLCertificateKeyFile  /etc/apache2/ssl_keys/localhost+1-key.pem
```

Dockerfileに追加

```dockerfile:Dockerfile

#SSL設定
ADD ./ssl.conf /etc/apache2/sites-available/ssl.conf
ADD ./localhost+2-key.pem /etc/apache2/ssl_keys/localhost+2-key.pem
ADD ./localhost+2.pem /etc/apache2/ssl_keys/localhost+2.pem
```

### nodebrewのインストール設定
参考URL
https://shioleap.github.io/blog/Linux/Debian/install-nodejs-on-nodebrew/

nodebrewインストール時に以下のメッセージが表示されるそうです。
参考URLの言う通りにコマンドを打つときに面倒なので、この設定をしたいと思いました。

Dockerfileに追加

```dockerfile:Dockerfile
#nodebrewのインストール
RUN curl -L git.io/nodebrew | perl - setup
RUN echo 'export PATH="$HOME/.nodebrew/current/bin:$PATH"' >> ~/.bashrc && . ~/.bashrc
```

### Apacheの起動設定

最後にDockerfileにApacheをフォアグランドで起動する命令を書きます。

```dockerfile:Dockerfile
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
```

## docker-compose.ymlファイルの作成

ざっくりこんな感じで設定

```yml
version: '2'
services:
  db:
    image: mysql:5.7
    # ./data/db（ホストOS）にデータが保持される
    volumes:
      - "./data/db:/var/lib/mysql"
    env_file: .env
  httpd:
    build: ./docker/web
    depends_on:
      - db
    ports:
      - "80:80"
      - "443:443"
      - "3000:3000"#browser-sync用
      - "3001:3001"#browser-sync用
    privileged: true
    #ドキュメントルートを変更する設定
    #左はローカルマシンのディレクトリ
    #右はコンテナ内のディレクトリ
    #ここは任意のディレクトリを指定します（本番環境に合わせてもOK）
    #Dockerfileとssl.confにもDocument Rootを設定する箇所あり
    volumes:
       - "./public_html:/home/XXXX/public_html/www"
  smtp:
    image: schickling/mailcatcher
    ports:
      - "1080:1080"#MailCatcher
      - "1025:1025"#smtp用
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    env_file: .env
    links:
      - db
    ports:
      - 8080:80
    volumes: 
      - "./phpmyadmin/sessions:/sessions"
```

.envはデータベース用の設定ファイル

```
MYSQL_RANDOM_ROOT_PASSWORD=yes
MYSQL_DATABASE=[データベースの名前]
MYSQL_USER=[ユーザー]
MYSQL_PASSWORD=[パスワード]
```


## docker-syncで高速になって幸せに - docker-syncの設定

- docker for macはホスト側のvolumeをマウントしていると劇的に遅い
- docker toolboxをゴニョゴニョすればできるらしい。
- docke-syncを使うと劇速になるらしい。しかも割と簡単。

ということで、docker-syncを採用してみました。
参考URL
https://qiita.com/Satoshi_Numasawa/items/278a143aa41735e1b0da
https://github.com/EugenMayer/docker-sync
https://github.com/EugenMayer/homebrew-dockersync

まずはインストールします。

```bash:terminal
$ sudo gem install docker-sync
brew install eugenmayer/dockersync/unox
brew install fswatch unison rsync
```

Macに初めから入っているrubyだと権限のからみでsudoでインストールしないとpermission deniedで怒られることがあります。
＊sudoでインストールしても問題なく動きました。

### docker-sync.ymlにてシンクするホストのディレクトリと同期方法を指定する

「sync_strategy」にて同期の方向を決めます。
- rsync : ホスト→コンテナへの同期
- unison : 双方向の同期

```yml:docker-sync.yml

version: '2'

syncs:
  sync-volume:
    src: './public_html'
    sync_strategy: 'unison'
```

### docker-compose-dev.yml

docker-sync.ymlの設定内容をdocker-compose.ymlを上書き設定するためのファイルの設定

```yml:docker-compose-dev.yml
version: '2'

volumes:
  sync-volume:
    external: true

services:
  httpd:
    volumes:
      - sync-volume:/home/webmaster/public_html/www
```

## まずは、imageをBuildします

```bash:Terminal
$ docker-compose pull
$ docker-compose build --no-cache
```

## docker-syncを起動してからコンテナを作成します

```bash:Terminal
$ docker-sync start
$ docker-compose -f ./docker-compose.yml -f ./docker-compose-dev.yml up -d
```

これでやっとLAMP+ssl+メールテスト環境の出来上がりました。

mailCatcherでメール内容を確認する場合は、 "http://localhost:1080" とすると確認できます。
参考URL
https://mailcatcher.me/

## node.jsのインストール

全部Dockerfileに書いてしまおうかと思っていましが、開発の環境によって色々かわるかもと思ったのでとりあえず手動でいいかなと。
あとあまり突っ込みすぎると up --buildの時に時間がかかりすぎるかななど。
＊単に面倒になってきただけかも。

もしDockerfileに記述してup --buildでnode.jsのインストールまで行いたい場合はこちらが参考になるかと思います。

https://qiita.com/kenshiroh/items/f8702086e3ae29cfc350
OSが違いますが、最後の２行が参考になります。

### コンテナへのログイン

コンテナへのログインは、

```bash
$ docker exec -it {コンテナ名} /bin/bash
```
でできます。

例として

```bash
#まずコンテナ名を調べます
$ docker-compose ps
        Name                       Command               State                                           Ports
-------------------------------------------------------------------------------------------------------------------------------------------------------
php-node_db_1           docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp
php-node_httpd_1        docker-php-entrypoint /usr ...   Up      0.0.0.0:3000->3000/tcp, 0.0.0.0:3001->3001/tcp, 0.0.0.0:443->443/tcp,
                                                                 0.0.0.0:80->80/tcp
php-node_phpmyadmin_1   /run.sh supervisord -n -j  ...   Up      0.0.0.0:8080->80/tcp, 9000/tcp
php-node_smtp_1         mailcatcher --no-quit --fo ...   Up      0.0.0.0:1025->1025/tcp, 0.0.0.0:1080->1080/tcp
#↑こんなのでます。
$ docker exec -it php-node_httpd_1 /bin/bash　#return
```

ログインしたらディレクトリは初期設定の/var/www/htmlになっています。

まずはnode.jsのリモートの一覧を表示

```bash
$ nodebrew ls-remote
```

使いたいバージョンのnode.jsをインストール

```bash
#v10.15.0での例
$ nodebrew install-binary v10.15.0
# 10.15.0を使うように指示
$ nodebrew use v10.15.0
```

## Gulpの設定

## 作業ディレクトリでnpmの初期化

```bash
#もし設定でドキュメントルートを変更していましたら、そちらに移動します。
$ cd /home/XXXX/public_html/www
$ npm init -y
```

package.jsonにrepositoryのエラーが出ないようにするために記述を追加

```bash
$ vi package.json
```

```json
{
  "name": "www",
  "version": "1.0.0",
  "main": "gulpfile.js",
  "dependencies": {},
  "private": true,←これ
  "devDependencies": {
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "keywords": [],
  "description": ""
}
```

### Gulpをインストールする

```bash
$ npm istall —save-dev gulp
```

gulpへのパスが通らない場合があるので、パスを通す

```bash
$ npm link gulp
#これ一発でOK!簡単！
```

#### その他のパッケージもインストールする

```bash
npm install —save-dev gulp-sass
```

などなど。

インストールの時にこんなエラーが出ます。

```bash
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules/chokidar/node_modules/fsevents):
```
これは、MacOS限定のモジュールだそうで、このエラーはそれほど気にしなくていいかもしれません。
もし気になるようでしたら、

```bash
npm install --no-optional XXXX
```
と--no-optionalをつけるといいそうです。
*個人的にはこのエラーは気にならなかったので、試してみてないです。

参考URL
https://mimirswell.ggnet.co.jp/blog-221

####一発でパッケージをインストールする方法

シェルスクリプトを書いて一発でインストールしちゃいます。
npm.shに以下を記述します。

```sh
#!/bin/sh

for i in `cat npm_module_list.txt`
do
  npm install --save-dev $i
done
```

npm_module_list.txtファイルに必要なパッケージを書いていきます。
例)

```text
gulp
gulp-sass
browser-sync
gulp-notify
gulp-plumber
gulp-sourcemaps
gulp-group-css-media-queries
gulp-autoprefixer
gulp-clean-css
gulp-uglify-es
gulp-rename
gulp-inline-source
```

```bash
$ bash npm.sh
```



#### browser-syncについて

proxyの設定でlocalhostを指定したら上手くいきました。
あとはdocker-compose.ymlにてポート３０００と3001の設定をしてあればOK

```javascript
gulp.task('browser-sync',function(done){//browser-syncのタスク設定
   browserSync({
       proxy:"localhost",
	   open: false
   });
   done();
});
```

#### gulp.watchについて

Gulpのその他の設定は参考文献がたくさんあるので、そちらを参照してgulpfile.jsを作っていけば良いかと思います。
ただDockerコンテナのDebianなどでbrowser-syncを使うと、指定したファイルが更新しても、browser-syncの方で認識しないようです。

その場合はポーリングの設定をするといいとの情報がありました。
具体的には、gulpfile.jsで記述する際に

```javascript
gulp.watch(paths.scss + '**/*.scss',{ usePolling: true }, gulp.series('scss'));
```
とするといいようです。

参考URL
https://github.com/floatdrop/gulp-watch/issues/213
https://www.kimurak.net/wordpress/angularjs/2372/

これで、快適開発環境が作れました！！

## 追記 : 思ったらなんか"gulp.watchが遅い!!!ので調べた

~~https://blog.hanhans.net/2017/05/23/docker-for-mac-slow/~~

~~ホスト側のディレクトリのマウントに関して前から問題が出ていたんですね。~~

~~なんか方法があるようですが、ちょっとハードルが高い。~~
~~しばらくはコンテナ内でgulp.watchは避けた方がいいかも…。~~
~~せっかくまとめましたが、node.jsをコンテナに入れてという方法はしばらく封印します。~~

[docker-syncの方法を追記しました。](https://qiita.com/soundweaver/items/615f2cac37d387661dc2#docker-sync%E3%81%A7%E9%AB%98%E9%80%9F%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%A6%E5%B9%B8%E3%81%9B%E3%81%AB---docker-sync%E3%81%AE%E8%A8%AD%E5%AE%9A)

とても快適です。