【Docker】マルチサイト構築（WrodPress,Ruby on Rails,Let'sEncrypt,nginx-proxy,postfix,MySQL）

# はじめに
Dockerによるマルチサイト構築手順です。
WordPress、Ruby on Railsでマルチサイトを構築です。
投稿者の環境は、CentOS7環境です。

# できること
* WordPress、Ruby on RailsのWebサイトをマルチサイトで公開する。
* サイト数は、追加、削除が容易に行える。
* Webサイトは、Let'sEncryptでSSL化して公開する。
* １つのMySQLコンテナを共通DBとして利用する。 
* Ruby on Railsの既存プロジェクトをDocker上で動作させる。
* Ruby on Railsコンテナに対して、dbcreate、migration、seed投入、アセットプリコンパイルする。
* ホストOSのCronスケジュールで、Ruby on Railsコンテナ上のバッチスクリプトをスケジュール実行する。
* OS起動時にコンテナは自動起動にする。
* postfixコンテナでsmtpサーバを建てる。<br>
  WordPress、ruby on railsからsmtp接続設定を行い、メール送信する。


上記が実現できれば、一連のWebサービス公開できると思います。

# マルチサイトの完成図
![システム概要図.jpg](https://qiita-image-store.s3.amazonaws.com/0/117828/2c760132-50ca-4819-db27-92557d671ce6.jpeg)
>投稿者の環境は、サーバ２台を利用し、postfixコンテナだけ、さくらVPSで稼働する。


# コンテナの構成
|コンテナ|使用イメージ|内容|
|:---|:---|:---|
|MySQL|mysql:5.7|コンテナで使用する共通DB|
|WordPress<br>（１番目）|wordpress:latest|WordPressのWebサイト|
|WordPress<br>（２番目）|wordpress:latest|WordPressのWebサイト|
|Ruby on Rails|ruby:2.4.1|Ruby on RailsのWebサイト|
|nginx-proxy|jwilder/nginx-proxy|リバースプロキシ、１台のサーバで複数Webサイトをホストする|
|letsencrypt-nginx-proxy|jrcs/letsencrypt-nginx-proxy-companion|nginx-proxyと同時使用し、Webサイトの環境変数のVIRTUAL_HOSTを定義することで、WebサイトをSSL化する|
|postfix|catatnight/postfix|smtpサーバ|

# 準備ファイル
順を追って説明するが、下記ファイルを作成する。

```
/home/web
│
├── docker-compose.yml #コンテナの定義ファイル
│
├── mywebservice.service #OS起動時のコンテナ自動起動のサービス定義ファイル
│
├── rails #配下にRuby on Railsのプロジェクトを配置
│   └── rails_web1 #Ruby on Railsサイト１つ目
│       ├── Dockerfile #Ruby on RailsサイトのDockerfile 
│       └── src #Ruby on Railsサイトのソース一式
│
└── sql #Mysqlコンテナ初期起動時に実行するスクリプト
    └── docker-entrypoint.sh #WordPress２つ目以降のDB作成スクリプト
```

# docker-compose up後のフォルダ構成

```
/home/web
│
├── certs #LetsEncryotの証明書配置
│   └── ・・・
│
├── docker-compose.yml #コンテナ定義
│
├── mysql #MySQLデータ
│   └── ・・・
│
├── mywebservice.service #OS起動時のコンテナ自動起動サービス定義
│
├── rails #Ruby on Railsのプロジェクトルート
│   └── rails_web1 #Ruby on Railsサイト１つ目
│       ├── Dockerfile #Ruby on RailsサイトのDockerfile 
│       └── src #Ruby on Railsサイトのソース一式
│           ├── config
│           │   ├── database.yml
│           │   └── environments
│           │       ├── development.rb
│           │       ├── production.rb
│           │       └── ・・・
│           └── ・・・
│ 
├── sql #MySQLコンテナ初回起動時スクリプト
│   └── docker-entrypoint.sh #WordPress２つ目以降のDB作成スクリプト
│ 
└── wordpress #WordPressのフォルダ
    ├── wp_1 #Wordpressサイト１つ目
    │   └── wp-content
    │       └── ・・・
    │    
    └── wp_2 #Wordpressサイト２つ目
        └── wp-content
            └── ・・・
        
```

# 章立て
１．事前準備
２．WordPressサイトを２つ立ち上げる。
３．Let'sEncryptでWordPressサイトをSSL化する。
４．Ruby on Railsサイトを立ち上げる。
５．Ruby on Railsのバッチ処理をCronでスケジュール実行する。
６．postfixのメールサーバを立ち上げる。
７．WebサイトをOS起動時に自動起動にする。


# １．事前準備
Docker環境インストール
Docker-Compose環境インストール
>Dockerサービス開始、自動起動を設定
>
```
#systemctl start docker #Dockerサービス開始
```
>
```
#systemctl enable docker #Docker自動起動
```

# ２．WordPressサイトを２つ立ち上げる
（１）docker-compose.yml　に導入するコンテナを定義する。

```
#vi docker-compose.yml
```

```docker-compose.yml
version: '2'
services:
  db:
    container_name: mysql
    image: mysql:5.7
    ports:
      - 3306:3306
    volumes:
      - ./sql:/docker-entrypoint-initdb.d
      - /home/web/mysql:/var/lib/mysql
    environment:
      MYSQL_ROOT_USER : root
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: wp_1
      MYSQL_USER: wp_1
      MYSQL_PASSWORD: password
    restart: always
 wordpress1:
    container_name: wp_1
    depends_on:
      - db
    image: wordpress:latest
    volumes:
      - /home/web/wordpress/wp_1/wp-content:/var/www/html/wp-content
    external_links:
      - db
    expose:
      - 80
    environment:
      VIRTUAL_HOST: XXXXXX.XXX #WordPress１つ目のドメイン名
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: wp_1
      WORDPRESS_DB_USER: wp
      WORDPRESS_DB_PASSWORD: password
    restart: always
 wordpress2:
    container_name: wp_2
    depends_on:
      - db
    image: wordpress:latest
    volumes:
      - /home/web/wordpress/wp_2/wp-content:/var/www/html/wp-content
    external_links:
      - db
    expose:
      - 80
    environment:
      VIRTUAL_HOST: XXXXXX.XXX #WordPress２つ目のドメイン名
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: wp_2
      WORDPRESS_DB_USER: wp
      WORDPRESS_DB_PASSWORD: password
    restart: always
nginx-proxy:
    container_name: nginx_proxy
    image: jwilder/nginx-proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/nginx/vhost.d
      - /usr/share/nginx/html
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - restart: always
networks:
  default:
    external:
      name: net-proxy

```
>nginx-proxyコンテナは、リバースプロキシです。VIRTUAL_HOSTの定義を自動で探して、Webサイトを公開してくれます。

（２）２つ目のDB作成
MySQLコンテナは、初回起動時に初期DBは作成してくれるが、２番目以降のDBは作成しない。
今回、WordPressサイト毎にDBを用意するため、２つDBを用意する。
２つ目以降のDB作成方法として、MySQLコンテナ内のエントリーポイントを利用する。
このエントリーポイントのフォルダにシェルやSQLを配置しておくと、これらを初回起動時に実行してくれる。
この仕組を利用し、WordPressの２番目以降のDBを作成する。

docker-compose.ymlに、エントリーポイントのフォルダ記載を行い、ホスト側のエントリーポイントにシェルを配置する。

```
services:
  db:
    #---途中省略---
    volumes:
      - ./sql:/docker-entrypoint-initdb.d
    #---途中省略---
```

```
#mkdir /home/web/sql -p
#vi /home/web/sql/docker-entrypoint.sh
```

```docker-entrypoint.sh
echo "CREATE DATABASE IF NOT EXISTS \`wp_2\`;" | "${mysql[@]}"
echo "GRANT ALL ON \`wp_2\`.* TO '"$MYSQL_USER"'@'%';" | "${mysql[@]}"
echo 'FLUSH PRIVILEGES;' | "${mysql[@]}"
```

（３）コンテナ用のネットワーク作成

```
#docker network create net-proxy
```

（４）コンテナ起動

```
#docker-compose up -d
```

（５）ブラウザで動作確認
ブラウザでアクセスすると、WordPressのサイト１、サイト２が表示される。

# ３．Let'sEncryptでwordpressサイトをSSL化する。
letsencrypt-nginx-proxy-companionコンテナを導入する。
WordPressコンテナのenvironmentに以下の2つを追加すると、Let'sEncryptでSSL化される。

* LETSENCRYPT_HOST
* LETSENCRYPT_EMAIL

（１）letsencrypt-nginx-proxy-companionコンテナを導入する。

```
#vi docker-compose.yml
```

```docker-compose.yml
      #---途中省略---
 wordpress1:
      #---途中省略---
     environment:
      #---途中省略---
      LETSENCRYPT_HOST: xxxxxx.xxx    #+追加行、WordPress１つ目のドメインのホスト名
      LETSENCRYPT_EMAIL: xx@xxxxxx.xxx    #+追加行、メールアドレス
      #---途中省略---
 wordpress2:
      #---途中省略---
     environment:
      #---途中省略---
      LETSENCRYPT_HOST: xxxxxx.xxx    #+追加行、WordPress２つ目のドメインのホスト名
      LETSENCRYPT_EMAIL: xx@xxxxxx.xxx    #+追加行、メールアドレス
letsencrypt-nginx-proxy-companion: #+追加行
    image: jrcs/letsencrypt-nginx-proxy-companion #+追加行
    volumes: #+追加行
      - /home/web/certs:/etc/nginx/certs:rw #+追加行
      - /var/run/docker.sock:/var/run/docker.sock:ro #+追加行
    volumes_from: #+追加行
      - nginx-proxy #+追加行
    restart: always #+追加行
nginx-proxy:
      #---途中省略---
    volumes:
      - /home/home/web/certs:/etc/nginx/certs:ro #+追加行
      #---途中省略---
```

（２）コンテナ再起動

```
#docker-compose restart
```

（3）ブラウザで動作確認
ブラウザでアクセスすると、WordPressのサイト１、サイト２がSSL化されて表示される。

# 4．Ruby on Railsサイトを立ち上げる。
Ruby on Railsサイトの既存プロジェクトをDocker環境に移行し、稼働させる。

（１）Ruby on Railsのコンテナを追加する。

```
#vi docker-compose.yml
```

```docker-compose.yml
  rails_web1: #+追加行
    build: #+追加行
      context: ./rails/rails_web1 #+追加行、rubyコンテナのDockerfileの場所
      dockerfile: Dockerfile #+追加行、rubyコンテナのDockerfile
    container_name: rails_web1 #+追加行
    #command: bundle exec rails s -p 80 -b '0.0.0.0' -e production #+追加行、developmentの場合
    command: bundle exec rails s -p 80 -b '0.0.0.0' -e development #+追加行、productionの場合
    environment: #+追加行
      VIRTUAL_HOST: xxxxxx.xxx    #+追加行、Ruby on Railsサイトのドメイン名
      LETSENCRYPT_HOST: xxxxxx.xxx    #+追加行、Ruby on Railsサイトのドメインのホスト名
      LETSENCRYPT_EMAIL: xx@xxxxxx.xxx    #+追加行、メールアドレス
    volumes: #+追加行
      - ./rails/rails_web1/src:/myapp #+追加行
    expose: #+追加行
      - 80 #+追加行
    depends_on: #+追加行
      - db #+追加行
    restart: always #+追加行
```

（２）Dockerfileを定義する。

```
#mkdir -p ./rails/rails_web1/src
#vi ./rails/rails_web1/Dockerfile
```

```Dockerfile.
FROM ruby:2.4.1
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev
RUN mkdir /myapp
WORKDIR /myapp
ADD ./src/Gemfile /myapp/Gemfile
ADD ./src/Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
ADD ./src /myapp
```

（３）Ruby on Railsの既存ソース一式をホスト側の「src」ディレクトリに配置する。

```
/home/web
└── rails #配下にRuby on Railsのプロジェクトを配置
    └── rails_web1 #Ruby on Railsサイト１つ目
        ├── Dockerfile #Ruby on RailsサイトのDockerfile 
        └── src #Ruby on Railsサイトのソース一式
```

（４）Ruby on RailsのDB定義を行う。
docker-compose.ymlに記載したServiceのDB情報をdatabase.ymlに記載する。

```
vi /home/web/rails/rails_web1/src/config/database.yml
```

```database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: password
  host: db 
  database: db_rails
development:
  <<: *default
production:
  <<: *default
```

（５）コンテナ再起動

```
#docker-compose restart
```

（６）ブラウザで動作確認
ブラウザでアクセスすると、Ruby on RailsサイトがSSL化されて表示される。

（７）db_create、migration、seed投入、アセットプリコンパイル
Ruby on Railsサイトに対して、db_create、migration、seed投入、アセットプリコンパイルを行う。

```
docker exec rails_web1 bundle exec rake db:create db:migrate db:seed
docker exec rails_web1 bundle exec rake assets:precompile RAILS_ENV=production
```

# 5．Ruby on Railsのバッチ処理をCronでスケジュール実行する。

コマンドラインからのバッチ実行で動作するかを確認する。

```
#docker exec rails_web1 bundle exec rake cron:xxxxx RAILS_ENV=production
```

動作に問題なければ、ホスト側のcrontabに登録する。

```
#crontab -e
```

```
0 18 * * * docker exec rails_web1 bundle exec rake cron:xxxxx RAILS_ENV=production
```

# 6．postfixのメールサーバを立ち上げる。
（１）catatnight/postfixのコンテナを稼働する。


```docker
docker run -p 25:25 \
         -e maildomain=xxxxxx.xxx -e smtp_user=user:password \
         --name postfix -d catatnight/postfix
```

>[catatnight/postfix](https://github.com/catatnight/docker-postfix)

>dockerで起動したメールサーバコンテナのローカルIPアドレスは、以下で取得できます。1台のサーバ内でWebサーバ、メールサーバ等が同居して動かしている場合は、ここで取得するローカルIPがメールサーバのIPアドレスとなります。
>docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' コンテナID


（２）Ruby on Railsのメール送信を設定する。
Ruby on RailsのAction Mail設定を行う。

```
vi /home/web/rails/rails_web1/src/config/environments/production.rb
```

```production.rb
  #Error キャッチ有効化※メールが送信できない時の調査用
  config.action_mailer.raise_delivery_errors = true

  #メールテスト送信設定
  config.action_mailer.smtp_settings = {
        :address => "xxxxxx.xxx",
        :port => 25,
        :domain => 'xxxxxx.xxx',
        :user_name => "user@xxxxxx.xxx", #アドレス
        :password => "password", #パスワード
        :openssl_verify_mode  => 'none' #SSL無効
  }
```

設定後、Ruby on Railsサイトからメール配信を行い、正常にメール送信する事を確認する。

（３）WordPressのメール送信を設定する。
WordPressに「WP Mail SMTP」プラグインを導入し、メール送信サーバを設定する。
![wp smtp3.png](https://qiita-image-store.s3.amazonaws.com/0/117828/d8c2f18c-a519-ebe9-34ff-5293e55852ce.png)

WordPress側のSMTP設定
![wp smtp0.png](https://qiita-image-store.s3.amazonaws.com/0/117828/c15350ef-b7bb-d6e7-8b6e-017de1e1147e.png)

![wp smtp1.png](https://qiita-image-store.s3.amazonaws.com/0/117828/8b9c4212-abbb-d11d-c752-36001917252e.png)

WordPress側のSMTP送信テストを行い、正常にメール送信することを確認する。
![wp smtp2.png](https://qiita-image-store.s3.amazonaws.com/0/117828/d8cb3589-68fe-8fe0-3873-85a55b53e3f2.png)

# 7．WebサイトをOS起動時に自動起動にする。
（１）サービスファイルを作成する。

```
#vi /home/web/mywebservice.service
```

```
[Unit]
Description=My Service
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/home/web
ExecStartPre=/usr/local/bin/docker-compose stop
ExecStartPre=/usr/local/bin/docker-compose rm
ExecStartPre=/usr/local/bin/docker-compose down
ExecStartPre=rm /home/web/rails_web1/src/tmp/pids/server.pid -f
ExecStart=/usr/local/bin/docker-compose up -d

[Install]
WantedBy=multi-user.target
```

（２）サービスファイルを有効にする。

```
#cp /home/web/myservice.service /etc/systemd/system/
#systemctl daemon-reload
#systemctl enable myservice.service
#systemctl start myservice.service
```