kintoneクローン？ OSSWebDBのExmentをLightsail+Dockerで立ち上げたら結構ハマったのでメモしておく

## Exment is 何？
![2020-09-08_11h46_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116223/3e70140b-a7da-48ea-f5d3-b2e8276747b8.png)
Exmentは、Laravel製（ということは、LAMPですね。ということは、レンタルサーバーでも動きますね。）のWeb DBシステムです。リレーショナルなデータベースをGUI管理画面から操作できます。

ExmentはOSSで、GPLライセンスです。開発は株式会社カジトリ​が中心となって行っています。詳細は[開発者の方の記事をご覧ください](https://qiita.com/hirossyi73/items/b6280901c394216c07b8)。

GUIのWeb DBというと、なんとなくサイボウズ社のkintoneを連想する方もいるのではないでしょうか。

開発元からはキントーンのキの字も聞こえてきませんが、筆者はひそかにkintoneクローンだと思っていて、かなり可能性を秘めているOSSプロダクトだと考えています。

正直、かなりほれ込んでいるのですが、この記事ではお気に入りポイントは解説しません。また別の機会に。

## クラウドでなるべくお安くExmentを運用したい！

ローカルでは、[Dockerを使ってExmentを試す](https://qiita.com/yamada28go/items/c5b8173be71df9bc1ab7)ことができます。作ってくれた人ありがとう！

ローカルで実際やってみたところ（macOS Mojave、Windows 10 Home WSL2で確認）、あっさり出来ました。すごいですね。

そうしたら、クラウドでホスティングして、外出先などからもアクセスしたくなるというのが人情ですよね。

ホスティング先をいろいろ考えてみました。フロントエンドだけなら、NetlifyやVercelといった無料のサービスもあるのですが、残念ながらこれはバックエンドのシステムです…

先述していますが、Exmentはレンタルサーバーでも運用できます。

[Exment公式サイトには、さくらやエックスサーバーといったレンサバでのインストール手順](https://exment.net/docs/#/ja/install_rental)が公開されていますが、残念ながら、さくらやエックスサーバーのアカウントを私は持ち合わせておりません。つまり、筆者はレンサバの契約をしていないのですね。（フロント寄りの作りかけ個人プロダクトがNetlifyやVercelにはわんさかあるんですけど）

Exmentのためだけにレンサバ借りるのもなんだかなあ、だったらVPSのほうが良くない？　と色々調べた結果、AmazonのLightsailが私の用途にぴったりだと気付きました。

（なお、筆者はこの時点でVPS、AWS童貞です）

## ではさっそくLightsailのアカウントを取得して、始めるぞ！
Lightsailにログインしてからインスタンスの作成あたりまでは、下記記事が詳しいです。

- [AWS Lightsailを使ってみた](https://qiita.com/nkojima/items/ffb369b19fc305537305)

今回は、一番安い$3.5/月の512MBRAMのインスタンスにしました。なるべくお安く、というテーマなので。どうしてもスペック足りなかったら、その時にスケールアップなりなんなり対応を考えようというスタンスです。

Lightsailには、WordPressなどのWebアプリケーションがセットになったインスタンスひな形があるのですが、当然知名度まだまだのExmentひな形というのはありません。

そこで、OSのみのインスタンスを選択します。今回は、CentoOS7を選択しました。

## CentOSにフツーにインストールする？ それとも…？
[Exment公式には、CentOS7にインストールする手順も公開](https://exment.net/docs/#/ja/install_linux)されており、またそれを補強する記事もあります。（下記参照）

- [オープンソースWebデータベース Exment をAWS Lightsail で利用する](https://assignment.co.jp/post/189545147785/%E3%82%AA%E3%83%BC%E3%83%97%E3%83%B3%E3%82%BD%E3%83%BC%E3%82%B9web%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9-exment-%E3%82%92aws-lightsail-%E3%81%A7%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B)

が、公式を見ていただければわかる通り、結構な手順があります。で、実際1度やりました。何度か環境を捨てたりやり直したりする過程で「この手順毎回やってられんわ！」となり、他の方法を考えることに至ります。

**せや、Dockerそのまま載せたったらええやん？**

## Lightsail+DockerでExmentを動かす（httpsもあるよ！）
ここからは、ハマりポイントを踏まえた回避策も含めて手順を記します。ここを守れば、最短でExment on Lightsail+Dockerが実現できます。

### CentOS7でインスタンス作成後、Docker、Docker-composeとgitをインストールする

では、もろもろインストールしていきます。

毎回、sudo つけるの大変なので、rootになります。

Lightsailでrootになるには、こちらの記事を参考にしました。

- [AWS lightsail インスタンスで root ユーザになる](https://lake-michigan.hatenablog.com/entry/toberootinlighsail)

```bash
$ sudo su -
```
Dockerをインストールします。こちらの記事が詳しいです。

- [【Docker入門】AWS lightsail上にdocker+docker-composeを入れてnginxにアクセスしてみる](https://qiita.com/t-ta_coco/items/1bdd8477705d251f8515)

```bash
[root@ip-172-xx-xx-x ~]# yum install -y docker          # dockerのインストール
[root@ip-172-xx-xx-x ~]# service docker start           # dockerの起動
[root@ip-172-xx-xx-x ~]# groupadd docker                # ユーザー権限で実行できるようにしておく
[root@ip-172-xx-xx-x ~]# usermod -g docker centos       # 作成したグループにcentosユーザを追加
[root@ip-172-xx-xx-x ~]# sudo /bin/systemctl restart docker.service
[root@ip-172-xx-xx-x ~]# docker info        
```
これでDockerはインストールできました。
続いて、Docker-composeです。

```bash
[root@ip-172-xx-xx-x ~]# curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
[root@ip-172-xx-xx-x ~]# chmod +x /usr/local/bin/docker-compose
[root@ip-172-xx-xx-x ~]# docker-compose --version
```
最後に、Gitをインストールします。CentOS7のyumで入るGitは、1.8.2と古いバージョンのものです。気になる人は、下記記事を参考に新しいのを入れてみてください。今回は、git cloneしたいだけなので、そのままyumでインストールします。

- [CentOS7にGit2系をyumでインストール](https://qiita.com/d-dai/items/3cc0c8c81911d5b6cce5)

```bash
[root@ip-172-xx-xx-x ~]# yum install -y git
```
これで、Gitもインストールできました。

## スワップ領域を作る
RAM512MBのインスタンスなので、DockerでMySQLまで動かすとメモリ不足になりました。（ハマりポイント１）なので、先んじてスワップ領域を作っておきましょう。

スワップの作り方は下記記事を参考にしました。

- [CentOS7 スワップファイル（swap file）の作成方法](https://qiita.com/wmx/items/631222f2e5a9e7a59fb4)

```bash
[root@ip-172-xx-xx-x ~]# dd if=/dev/zero of=/swapfile bs=1M count=4096 status=progress
```

コマンドの書き方をミスってたため、結構ハマりました（ハマりポイント２）

あれこれ試行錯誤した過程で、/dev/zeroが何のファイルかわかっておらず、rm -rfしちゃったのはいい思い出です。

/dev/zeroを復活させるのは、こちらの記事を参考にしました。

- [zeroデバイスの復活](https://okwave.jp/qa/q1477479.html)

```bash
[root@ip-172-xx-xx-x ~]# mknod -m 666 /dev/zero c 1 5
[root@ip-172-xx-xx-x ~]# chown root:mem /dev/zero
```

`status=progress`オプションですが、今回はスワップ領域が4Gと大きめなので、しばらくターミナルが沈黙しちゃうと心配です。なので、ddコマンドの進捗をお知らせしてくれるオプションをつけました。下記記事を参考にしています。

- [dd コマンドの進捗状況を表示する方法](https://qiita.com/cd01/items/3718089728e935b685c3)

ddでスワップ領域が作成されたら、粛々と残りの作業を進めます。

```bash
[root@ip-172-xx-xx-x ~]# chmod 600 /swapfile #パーミッションの変更
[root@ip-172-xx-xx-x ~]# mkswap /swapfile #スワップの作成
Setting up swapspace version 1, size = 1048572 KiB
no label, UUID=d0519bf6-8abf-4c0d-9375-8068c9e5e9a1
[root@ip-172-xx-xx-x ~]# swapon /swapfile #スワップの有効化
[root@ip-172-xx-xx-x ~]# free -m #スワップの確認
```

最後に、スワップの永続化をします。
viで/etc/fstabを開き、

```bash
/swapfile                                 swap                    swap    defaults        0 0
```

でおしまいです。

### git cloneで、docker-compose.ymlとDockerfileを拾ってくる。
先述ご紹介した、[ExmentのDockerを作ってくれた方のGithubから拾います](https://github.com/yamada28go/docker-exment)。

```bash
[root@ip-172-xx-xx-x /home/centos/]# git clone https://github.com/yamada28go/docker-exment.git
```

場所はどこでもいいと思うんですけど、デフォルトユーザーディレクトリ直下にしました。

### httpsは？
Dockerでhttpsというと、[https-portal](https://github.com/SteveLTN/https-portal)が有名なようです。

結論から先にいうと、**https-portalを先に設定した状態でExmentをインストールしたら、上手くいきませんでした！！！(ハマりポイント３)**

なので、SSLを設定する前に非SSLの状態でExmentのインストールを進めます。

一応、docker-compose.ymlに目を通しておきますか。

```docker-compose.yml
version: '3'
services:
  nginx:
    image: nginx:latest
    ports:
      - 8080:80
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - www-data:/var/www
    depends_on:
      - php

  php:
    build: ./php
    volumes:
      - www-data:/var/www
    depends_on:
      - db

  db:
    image: mysql:5.7
    ports:
      - 13306:3306
    volumes:
      - mysql-data:/var/lib/mysql
    environment:
      MYSQL_DATABASE: exment_database
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_USER: exment_user
      MYSQL_PASSWORD: secret

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - 8888:80
    depends_on:
      - db

# volumes を定義する
volumes:
  # volume の名前を指定
  # Exmentのインストールパス
  www-data:
    # Compose の外ですでに作成済みの volume を指定する場合は ture を設定する。
    # そうすると、 docker-compose up 時に Compose は volume を作成しようとしません。
    # かつ、指定した volume が存在しないとエラーを raise します。
    #external: true
  # mysql dbのインストールパス
  mysql-data:
```
あとでhttps-portalが80番ポートを使うので、nginxが8080になっているのは逆に都合がいいです。特にこのまま何も編集しません。

docker-compose.ymlとDockerfileは何もさわりませんが、php.iniだけは1行だけ追記します。

最後の行に

```vim
memory_limit=-1
```
を追記しておきます（ハマりポイント４、詳細は後述）

ではいよいよ…

```bash
[root@ip-172-xx-xx-x /home/centos/]# docker-compose up -d
```

初回なので、イメージの取得とビルド、ボリュームの作成をやってくれます。

起動が完了したら、`docker-comopse ps`で、全コンテナがUpしていることを確認します。（スワップを作っていなかった時は、ここでMySQLがExitしてました）

## 無事起動したら、IPアドレスでアクセスしてみよう
Lightsail側で以下の手順を済ませておきます。

- 静的IPのアタッチ
- ポート8080番の開放

xx.xx.xx.xx:8080にブラウザでアクセスしてみて、Laravelの初期画面が表示されていれば成功です。

xx.xx.xx.xx:8080/adminにアクセスすると、Exmentの初期設定画面になります。

画面は3部構成になっていて、

1. 言語とタイムゾーンの指定
1. DB設定
1. DB設定開始ボタン

の3画面です。何か入力するのは2画面目のDB設定です。

Dockerを使っていますので「[ExmentのDocker環境を作ってみた](https://qiita.com/yamada28go/items/c5b8173be71df9bc1ab7)」の記事を参考に、設定を埋めます。
>
- ホスト名　→　db
- データベース名　→　exment_database
- ユーザー名　→　exment_user
- パスワード　→　secret

この後、無事インストールが完了すれば、Exmentの初期画面となります。

## 添付ファイルとかはS3に保存したいじゃんね！

今回のLightsailインスタンスは、保存領域が20GBと、まあ何か動かすのはいいけど、バイナリデータ突っ込んでいったらすぐパンパンになるよね、って容量です。

Exmentには、外部のストレージサービスに各種ファイルを保存するオプションがあります。

- [(上級者向け)ファイルの保存先変更](https://exment.net/docs/#/ja/additional_file_saveplace)

せっかくですので、AWSでそろえて、S3に保存させるようにしてみましょう。

S3も童貞でしたので、下記記事を参考にバケットを作成してみました。

- [AWS S3のバケットの作り方](https://qiita.com/miriwo/items/41e488b79fb58fa7c952)

Exment側でも設定が必要です。既に`docker-compose exec php bash`で立ち上がっているコンテナの中に入ります。

```bash
[root@ip-172-xx-xx-x /home/centos/]# docker-compose exec php bash
```

これで、Exmentが動いているphpコンテナの中に入れます。プロンプトの表示がこんな感じに変われば成功です。

```bash
root@f54bef27801a:/var/www#
```

つまり、クラウド上にあるLightsailの中にあるDockerコンテナphpの中にいるという二重構造なわけですね。

引き続き、「[(上級者向け)ファイルの保存先変更](https://exment.net/docs/#/ja/additional_file_saveplace)」を読み解きながら、Exment（Laravel）のファイルを修正していきます。

Exmentルートフォルダにある、.envファイルをvimで修正/追記します。

```.env
EXMENT_DRIVER_EXMENT=s3
EXMENT_DRIVER_BACKUP=s3
EXMENT_DRIVER_TEMPLATE=s3
EXMENT_DRIVER_PLUGIN=s3

AWS_ACCESS_KEY_ID=(AWS S3のアクセスキー)
AWS_SECRET_ACCESS_KEY=(AWS S3のシークレットアクセスキー)
AWS_DEFAULT_REGION=(AWS S3のリージョン)

AWS_BUCKET_EXMENT=(添付ファイルで使用するAWS S3のバケット)
AWS_BUCKET_BACKUP=(バックアップで使用するAWS S3のバケット)
AWS_BUCKET_TEMPLATE=(テンプレートで使用するAWS S3のバケット)
AWS_BUCKET_PLUGIN=(プラグインで使用するAWS S3のバケット)
```

なお、`EXMENT_DRIVER_xxx`はすべて設定する必要はなく、必要があるものだけ設定します。それに紐づく`AWS_BUCKET_xxx`も必要なものだけでOKです。

私は添付ファイルとバックアップデータはS3に保存したかったので、

- `EXMENT_DRIVER_EXMENT`
- `EXMENT_DRIVER_BACKUP`
- `AWS_BUCKET_EXMENT`
- `AWS_BUCKET_BACKUP`

の4つのみ設定しました。

最後に、composerでライブラリをインストールします。

先ほど、`docker-compose up -d`の前にphp.iniを編集しましたが、Composerはメモリ食いで、すぐPHPのメモリ利用可能上限に達してしまうので、この上限を解除する設定をしたのでした。

Composerはインストールに時間がかかるので、時間かかった結果失敗した、って辛いですよね…（筆者はとても悲しかったです。）なので、この設定は`composer require`をする前にもう一度確認しておきましょう。

```bash
root@f54bef27801a:/var/www# php -i | grep memory_limit
memory_limit => -1 => -1
```

-1になっていればOKです。（メモリ上限なし、の意味）

では、いよいよライブラリのインストールです

```bash
root@f54bef27801a:/var/www# composer require league/flysystem-aws-s3-v3 ~1.0 -vvv
```

`-vvv`オプションは、もっとも粒度細かくログを出す、という意味の指示です。composerはインストールにとても時間がかかるので、長い時間ターミナルが沈黙していると心配になります。

なので、逐次ログを出してくれるようにします。このオプションをして長いこと沈黙があれば、それは本当に何かしらの理由で止まっているか、めちゃくちゃ重い処理をしている、ということになります…

S3に添付ファイルをアップするオプションの設定は以上です。適当な画像でもアップして、バケットの中にファイルがあることを確認しましょう。

## 最後に呪いのhttpsです…

いや、httpsに何の悪いところもないのですが、かなりハマって悩まされたので…

色々やり方が悪かったのかと思い、Docker環境全捨てからの再インストールを何度も行ったので、かなり時間を食ってしまいました。

さっきも言いましたが、docker-composeにhttps-portalも混ぜて書いてしまうと、初回インストールに失敗します。結局はよくわからんエラーに遭遇して詰みました。

インストールは3画面あるといいましたが、最後の3画面目のインストールボタンを押したら、あのLaravelのグレーみの強いエラー画面で

```
Use of undefined constant STDIN - assumed 'STDIN' (this will throw an Error in a future version of PHP)
```

って出てきて、DBにデータが差し込めないことに。PHPやLaravelに明るくない筆者であるため、ここで断念しました。

httpならちゃんとうまくいったので、結局このエラーをつぶすことはあきらめ、httpでインストール完了させ、その後httpsにするという作戦に切り替えた次第です。

### https-portalをdocker-compose.ymlに追記する

https-portalの部分だけ書き出します。

```docker-compose.yml
https-portal:
  image: steveltn/https-portal:1
  ports:
    - '80:80'
    - '443:443'
  links:
    - nginx
  restart: always
  volumes:
    - ./certs:/var/lib/https-portal
  environment:
    STAGE: 'production'
    DOMAINS: >-
      example.com -> http://nginx:8080
  depends_on:
    - php

```

特に難しいことはありません。公式のサンプルとそんなに変わりませんし。ポイントとしては、`enviroment:`の`STAGE:`が、`'production'`か`'staging'`でないと、Let's Encryptの本チャンの証明書はもらえないということです。ここがlocalとか指定なしだと、ローカル開発ってことで、なんちゃって証明書が発行されます。ここも地味にハマりポイントでした（ハマりポイント５）

さて、最後に https://example.com/ にアクセスして、Laravelの初期画面が表示されれば晴れて成功です…

と言いたいのですが、まだあります。

https://example.com/admin にアクセスすると、盛大に画面崩れを起こしているじゃないですか！

ブラウザコンソールを見ると、mixed contentでhttpがソースのCSS/JS類が全部ブロッキングされています。

これの対処が最初全然わからなかったのですが、試行錯誤の結果、LaravelおよびLaravel-adminが犯人と断定されました！　asset('/css/hoge.css')みたいな記述のところで、http付のパスを出力しちゃってるようです…

以下、この件についての対策です。また`docker-compose exec php bash`して中に入ってください。

```/var/www/exment/.env
APP_ENV=production
```
```/var/www/exment/app/Providers/AppProvider.php
class AppServiceProvider extends ServiceProvider
    public function boot()
    {
　　　　if (config('app.env') === 'production') {
  　　　　　\URL::forceScheme('https'); //Schemaじゃなくて、Scheme!!!（ハマりポイント６）
　　　　}
    }
```
```/var/www/exment/config/admin.php
    /*
    |--------------------------------------------------------------------------
    | Access via `https`
    |--------------------------------------------------------------------------
    |
    | If your page is going to be accessed via https, set it to `true`.
    |
    */
    'https' => env('ADMIN_HTTPS', true), // ←デフォはfalseなので、trueに！
```
これで、すべてのアセットがhttpsから配信され画面崩れなく表示されるようになりました。

正直、これが本当の意味で正解かどうかわかりません。もしかしたら、AppProvider.phpは不要かもしれません…　Laravelのバージョンによってコードの書き方が微妙に違うようでした。これは、本当にたくさんの参考ソースを読みまくって、なんとか動くコードに落ち着いたというのが実情です。

もし、もっと良い方法があるよ、という方はぜひコメント欄で教えてください。

## 以上、無事動くようになりました！
今のところ、機嫌よく動いてくれています。

## セキュリティ等
本記事は検証目的の環境づくりのため、セキュリティ等については触れておりません。詳しい方は補足いただけると助かります。

## 今後
Exmentの素敵なところをほめまくる記事とか書きたいですね！