GrowiをAWSに構築してみた


2018年末にSaaSとして利用できる新サービス「GROWI.cloud」がローンチされるというニュースが入ってきました。
こちらを待つのもありかなと思いましたが、早く以下の課題を解決したかったので、自社サーバに構築しました。

## 課題
今まではプロジェクト管理ツールに付いているwiki機能を、社内情報共有ツールとして使用していました。
しかし情報が蓄積していくにつれて、どこにあの情報があったかな、プロジェクトを横断する共通ナレッジはどこに置けばいいかな、という悩みが出てきました。

## 構成イメージ

![システム俯瞰図 (1).png](https://qiita-image-store.s3.amazonaws.com/0/85701/bdd92193-cc97-d7f4-f9fe-fe077a2ac651.png)



* growiアプリケーションだけはDockerコンテナ化せず、ホスト上で直接動かすようにしました。
* バックアップは公式の方法そのままで
    * https://docs.growi.org/admin-cookbook/mongodb-regular-backup

## 構築手順
* EC2に以下をインストールします
    * nginx
    * docker
    * docker-compose
    * node (=8.11.4)
    * npm (=5.x)
    * yarn (=>1.7)
    * forever
* growiとdocker-composeのソースコードをEC2に置く
* growi/package.jsonの中身を変更(growiをforeverでデーモン化して動かすようにした)

             "server:prod:ci": "npm run server:prod -- --ci",
        -    "server:prod": "env-cmd config/env.prod.js node src/server/app.js",
        +    "server:prod": "env-cmd config/env.prod.js forever start src/server/app.js",
             "server": "npm run server:dev",
             "start": "npm run server:prod",



## サービス起動

        $ cd {docker-compose.ymlがあるディレクトリ}
        $ docker-compose down
        $ docker-compose up -d
        $ cd {growiディレクトリ}
        $ yarn
        $ MONGO_URI=mongodb://localhost:27017/growi ELASTICSEARCH_URI=http://localhost:9200/growi HACKMD_URI=https://growi-hackmd.bizwind.co.jp HACKMD_URI_FOR_SERVER=http://localhost:3100 npm start


## 参考

### 公式のGrowiの更新を取り込む
* 公式GrowiリポジトリをForkして、upstreamブランチとして追従させます

        $ git remote set-url origin .....
        $ git remote add upstream git@github.com:weseek/growi.git 
        $ git remote -v show 

* 本家のGrowiの更新取り込み

        $ git fetch upstream 
        $ git remote prune origin

* オリジナルの変更を同期

        $ git fetch upstream 
        $ git merge upstream/master
        $ git push origin master

### 各種設定ファイルの内容
* nginx

        map $http_upgrade $connection_upgrade {
            default Upgrade;
            ''      close;
        }

        upstream growi {
            server localhost:3000;
        }

        server {
            listen 80;
            server_name xxx.xxx.xxx.xxx;
            root        /usr/share/nginx/html;
            index       index.html index.htm;

            location / {
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-Port $server_port;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass http://growi;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
                proxy_read_timeout 900s;
            }

        }

* docker-compose.yml

        version: '3'

        services:
          mongo:
            image: mongo:3.4
            ports:
              - 27017:27017
            volumes:
              - mongo_configdb:/data/configdb
              - mongo_db:/data/db

          elasticsearch:
            image: elasticsearch:5.3-alpine
            environment:
              - "ES_JAVA_OPTS=-Xms2g -Xmx2g"
            ports:
              - 9200:9200
            command:
              - sh
              - -c
              - "./bin/elasticsearch-plugin list | grep -q analysis-kuromoji || ./bin/elasticsearch-plugin install analysis-kuromoji;
                ./bin/elasticsearch-plugin list | grep -q analysis-icu || ./bin/elasticsearch-plugin install analysis-icu;
                /docker-entrypoint.sh elasticsearch"
            volumes:
              - es_data:/usr/share/elasticsearch/data
              - es_plugins:/usr/share/elasticsearch/plugins
              - ./esconfig:/usr/share/elasticsearch/config

          hackmd:
            build:
              context: ./hackmd
            environment:
              - GROWI_URI={growiのURL}
              - HMD_DB_URL=mysql://hackmd:hackmdpass@mariadb:3306/hackmd
              - HMD_CSP_ENABLE=false
            ports:
              - 3100:3000
            depends_on:
              - mariadb

          ##
          # MariaDB
          # see https://hub.docker.com/_/mariadb/
          mariadb:
            image: mariadb:10.3
            command: mysqld --character-set-server=utf8 --collation-server=utf8_general_ci
            environment:
              - MYSQL_USER=hackmd
              - MYSQL_PASSWORD=hackmdpass
              - MYSQL_DATABASE=hackmd
              - MYSQL_RANDOM_ROOT_PASSWORD=true
            volumes:
              - mariadb_data:/var/lib/mysql

          backup:
            image: weseek/mongodb-awesome-backup:0.2.0
            environment:
              - CRONMODE=true
              - AWS_ACCESS_KEY_ID={AWSアクセスキー}
              - AWS_SECRET_ACCESS_KEY={AWSシークレットアクセスキー}
              - S3_TARGET_BUCKET_URL={S3バケットURL}
            links:
              - mongo:mongo
            volumes:
              - ./crontab/root:/var/spool/cron/crontabs/root

        volumes:
          mongo_configdb:
            driver_opts:
              type: none
              device: /var/lib/docker/mount/mongo_configdb
              o: bind
          mongo_db:
            driver_opts:
              type: none
              device: /var/lib/docker/mount/mongo_db
              o: bind
          es_data:
            driver_opts:
              type: none
              device: /var/lib/docker/mount/es_data
              o: bind
          es_plugins:
            driver_opts:
              type: none
              device: /var/lib/docker/mount/es_plugins
              o: bind
          mariadb_data:
            driver_opts:
              type: none
              device: /var/lib/docker/mount/mariadb_data
              o: bind

### ハマりポイント
* Growiとは関係ありませんが、AWSのSSLワイルドカード証明書を使用する場合、S3のバケット名に「.」(ドット)を使わないほうがいいとの事。地味にハマった..
    * https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/BucketRestrictions.html

```
仮想ホスティング形式のバケットを Secure Sockets Layer (SSL) で使用する場合、SSL ワイルドカード証明書はピリオドを含まないバケットにのみ一致します。この問題を回避するには、HTTP を使用するか、または独自の証明書検証ロジックを記述します。仮想ホスティング形式のバケットを使用するときは、バケット名にピリオド (「.」) を使用しないことをお勧めします。
```

* AWSのELBでリバースプロキシしている場合、app:urlがhttpになり、HackMDのload-agentでjsエラーになる。
    * 「Failed to execute 'postMessage' on 'DOMWindow': The target origin provided」
    * よろしくない方法だと思いますが、強引にapp:urlの値をhttpsに固定
        * 参考: https://github.com/weseek/growi/issues/282
        * src/server/crowi/express-init.jsの「app:url」

                -    config.crowi['app:url'] = baseUrl = (req.headers['x-forwarded-proto'] == 'https' ? 'https' : req.protocol) + '://' + req.get('host');
                +    //config.crowi['app:url'] = baseUrl = (req.headers['x-forwarded-proto'] == 'https' ? 'https' : req.protocol) + '://' + req.get('host');
                +    config.crowi['app:url'] = baseUrl = 'https' + '://' + req.get('host');



## まとめ
* 社内ツールは定着までがゴールだと思うので、これから社内通知やポリシーの策定、ディレクトリ構成の検討といった事に取り組んでいきます
* 今のままでも十分活用できるツールですが、[本家Growi](https://github.com/weseek/growi)の更新も活発で、これからの動向が楽しみです