GROWI 7.0.2 をインターネットに繋がらないオンプレミス環境に構築する際に苦労した件

## はじめに

共有したい情報が多い場合など、社内のナレッジの整理に Wiki などが欲しくなってきます。人事異動前は、長らく [DokuWiki](https://www.dokuwiki.org/dokuwiki)を利用していましたが、[GitHub](https://github.com/) や [Qiita](https://qiita.com/)、[Mattermost](https://mattermost.com/)など、文章を Markdown で書くことが一般的（？）になっている時代に独自の記法を使用する wiki を利用することは、他の社員の学習コストが増えるため、避けたいところです。

DokuWiki にも [markdowku Plugin](https://www.dokuwiki.org/plugin:markdowku) など、Markdown を使えるようにするプラグインが存在しましたが、部分編集が本来の記法のようには使えない、HTML でのプレビューがリアルタイムに見えないなどの不満を感じました。

そんな中、Markdown が使える Wiki を探している中で[社内wiki選定にGROWIをおすすめする理由](https://zenn.dev/noxis/articles/fc4c456e183555)というページを見つけ、Growi[^1] について色々なページを見て、私のやりたいことにマッチしているという結論を得ました。

[^1]: 元々は、[Crowi](https://github.com/crowi/crowi)という Wiki から、フォークして crowi-plus → GROWI と進化したようです。（[最強のWiki「Crowi」のフォーク、「GROWI(旧crowi-plus)」を公開した話 #JavaScript - Qiita](https://qiita.com/yuki-takei/items/5df9601039ccfc1df01f)）

### 公式サイトが掲げる GROWI の特徴

公式サイト[OSS開発wikiツールのGROWI | 快適な情報共有を、全ての人へ](https://growi.org/ja/)で掲げられている特徴は、以下の通りです。概ね、Qiita でできることができそうです。

- Markdown 対応
  - 絵文字
  - TODOリスト(`- [ ] item`)
  - [MathJax](https://www.mathjax.org/) による数式
  - [PlantUML](https://plantuml.com/) による UML
  - [Marmeid](https://mermaid.js.org/)
- 図形作成（[draw.io](https://app.diagrams.net/) と連携）
- タグ検索・全文検索
- **同時多人数編集**[^2]
- 優れたカスタマイズ性
- グループ管理
- コメント機能
- 強力な認証機構(SSO にも対応)
- シンプルなアセット管理

特徴に挙げられていないですが、個人的にはページをツリー構造で管理出来るところが気に入っております。

[^2]:GROWI v6 までは、[HackMD](https://hackmd.io/)と連携することで実現していたようですが、[GROWI v7 では代わりにビルトインのエディタで同時多人数編集が可能になった](https://docs.growi.org/ja/admin-guide/upgrading/70x.html#%E5%BB%83%E6%AD%A2%E3%82%92%E4%BC%B4%E3%81%86%E4%BB%95%E6%A7%98%E5%A4%89%E6%9B%B4-hackmd-%E9%80%A3%E6%90%BA%E6%A9%9F%E8%83%BD%E3%81%AE%E5%BB%83%E6%AD%A2)そうです。

## 筆者が構築した環境

| Software | version |
| -------- | ------- |
| VMWare Workstation | 16 |
| OS | Ubuntu Server 24.04 LTS (VM) |
| GROWI | 7.0.2 |
| node.js | 20.12.2 |
| npm | 10.5.0 |
| yarn | 1.22.22 |
| mongodb | mongodb-org 6.0.15 |
| Elasticsearch | 8.13.2 |
| JDK | OpenJDK 17.0.10+7-1~22.04.1 |
| Nginx | 1.18.0-6ubuntu14.4 |
| Apache Tomcat | 10.1.23 |
| draw.io | 24.3.1 |

基本的にある程度、インターネットに繋がる別の環境で準備してから、社内環境に持っていく方法を取っています。

## Docker 使えたなら楽だったんだけど

職場では、管理者権限を保有していないので Docker 環境の構築ができず、元からある仮想環境(VMWare Workstation)で構築せざるを得ず、公式が推奨する [docker-compose による方法](https://docs.growi.org/ja/admin-guide/getting-started/docker-compose.html)は諦めました。（正式に採用が認められれば、その限りじゃないかもしれませんが…）

- [Dockerコンテナを持ち歩こう(Portable Docker) #Docker - Qiita](https://qiita.com/riafeed/items/c7729dc84191e93f0f3d)
- [uniras/Portable-Docker-for-Windows](https://github.com/uniras/Portable-Docker-for-Windows)(ポータブル環境を作成するためのスクリプト集)

のような情報もありましたが、今の [QEMU](https://www.qemu.org/) だとうまく目的の物を取り出せませんでした。（素直に別の環境にインストーラーでインストールして必要な物を取り出す手はありますが…）

[CentOS](https://www.centos.org/) はプロジェクトが終了してしまったので[Ubuntu Server](https://jp.ubuntu.com/)に構築することに決めました。[^3]

[^3]:まあ、[Debian](https://www.debian.org/)とか、[Fedora Linux](https://fedoraproject.org/)など、他のディストリビューションでも良いのでしょうが、ここは流行り（？）に乗ってしまいました :sweat_smile: 

が、公式のドキュメント [Ubuntu Server | GROWI Docs](https://docs.growi.org/ja/admin-guide/getting-started/ubuntu-server.html) が、かなりメンテナンスされていない… :cry:

:::note info
嘆いてるだけではしょうがないので自分で修正して Pull request を投げてみました。

- [Update for Ubuntu Server 22.04 LTS. by yasumichi · Pull Request #459 · weseek/growi-docs](https://github.com/weseek/growi-docs/pull/459)
- [Update for Ubuntu Server 22.04 LTS by yasumichi · Pull Request #460 · weseek/growi-docs](https://github.com/weseek/growi-docs/pull/460)

:calendar:2024/5/2　無事にマージされて公式ドキュメントが更新されました。 :smile: 
:::

## 必要な deb パッケージの取得とインストール

ここは特に苦労したところではないですが…

基本的には、

```
sudo apt install --download-only packagename
```

で `/var/cache/apt/archives` にダウンロードしたパッケージをオンプレミス環境に持っていって

```
sudo apt install /var/cache/apt/archives/*.deb
```

でインストールします。別に `/var/cache/apt/archives` に置かなくても良いのですが、apt の sandbox 環境からアクセスできないところに置くとインストールはできるものの、余計な警告が表示されるので…。

- [オフライン環境のUbuntuでapt install #Docker - Qiita](https://qiita.com/tetrar124/items/40fd9b1ff5f0b015a6da)
- [オフライン環境にaptでパッケージをインストールする - iyuniY’s blog](https://iyuniy.hatenablog.com/entry/2020/05/06/223940)

## Turborepo がオンプレミス環境にインストールできず…つか、必要だったの？

基本的に npm パッケージは、yarn のオフラインミラー機能を使って、一旦、インターネットに繋がる環境でパッケージをダウンロードして、オンプレミス環境に持っていきました。

- [Running Yarn offline | Yarn Blog](https://classic.yarnpkg.com/blog/2016/11/24/offline-mirror/)
- [【Yarn】パッケージをオフラインでインストールする #Node.js - Qiita](https://qiita.com/TakuyaHara/items/a2683c74c1827cb07583)

GROWI の [インストール](https://docs.growi.org/ja/admin-guide/getting-started/ubuntu-server.html#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB-3)まではこの方法で問題がありませんでした。

そして、[起動確認](https://docs.growi.org/ja/admin-guide/getting-started/ubuntu-server.html#%E8%B5%B7%E5%8B%95%E7%A2%BA%E8%AA%8D)のところで壁にぶち当たります。`npm start` を実行したところ、`turbo` ってコマンドなんかないよと npm 様がご立腹です。つか、`turbo` ってなんだっけ？

調べてる内に幸運にも[Ubuntu 22.04でGROWI 6.1.0を動かしてみる](https://zenn.dev/de_teiu_tkg/articles/70a8d5815f520b) というページを見つけました。

> build-essentialをインストール
> `$ sudo apt install build-essential`
> turboをインストール
> `$ sudo yarn global add turbo`
> そしてGROWIをインストールしていきます。

これは、公式にも含めてほしい情報ですね…。 :sweat_smile: 

皆さんは、ご存知かもしれませんが、Turborepo と呼ばれるインクリメンタルビルドツールのようです。

- [Turborepo](https://turbo.build/repo)
- [turbo - npm](https://www.npmjs.com/package/turbo)
- [Turborepo でモノレポ構成のプロジェクトを爆速でビルドする | DevelopersIO](https://dev.classmethod.jp/articles/turborepo-superfast-monorepo-ci/)

でこれをオンプレミス環境にインストールしようとしたんですが、うまくいきません。アーキテクチャ依存のパッケージは難なくインストールできるのですが、御本尊は、依存解決がいつまで経っても終わらず、インストールできませんでした。

諦めてビルドまでは、インターネットに繋がる環境で行うことにしました。

:::note info
それにしても global にインストールする必要あるのかな…。なんで依存関係として、package.json に入れないんだろ…。local に add しても script で実行できそうな気がするんだけど…（教えて、偉い人 :smirk: ）
:::

## 何故か Material Icons が表示されずレイアウトが崩れてしまった

無事にインターネットに繋がる環境で turbo をインストールし、ビルドが終わった環境をオンプレミス環境に移行しました。先にテストしておけば良かったのですが、その日は時間がなかったので :sweat: 

そして、`yarn app:server` で起動してみると悲しい状態に…

![bad_layout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36738/290a9d6f-ea86-f000-2517-eeb3e122b5ed.png)

ほとんどのボタンアイコンが表示されずに文字で表示されてレイアウトが崩れてしまい、操作性に難がある状態になってしまいました。ボタンの要素を調べてみると class が `material-symbols-*` となっていることから、Google の [Material Icons](https://fonts.google.com/icons) が表示される場所であると予想しました。

最初は、インターネットに繋がってないせいなのかと思いましたが、インターネットに繋がる環境でも同様の状態でした。

yarn したタイミングの npm パッケージリポジトリの状態が、GROWI のリリース時期と異なっていたせいなのか、私のビルド環境の構築に問題があったのか、真相は現段階では不明です…。

### 追記

ビルド時に以下のエラーが出ているのが直接の原因と思われます。

```
@growi/app:build: Failed to load font file: /opt/growi/apps/app/resource/fonts/MaterialSymbolsOutlined-opsz,wght,FILL@20..48,300,0..1.	
@growi/app:build: Error: Unknown font format
@growi/app:build: Failed to load font file: /opt/growi/apps/app/resource/fonts/SourceHanCodeJP-Regular-subset-main.woff2
@growi/app:build: Error: Unknown font format
@growi/app:build: Failed to load font file: /opt/growi/apps/app/resource/fonts/SourceHanCodeJP-Regular-subset-jis2.woff2
@growi/app:build: Error: Unknown font format
@growi/app:build: Failed to load font file: /opt/growi/apps/app/resource/fonts/PressStart2P-latin.woff2
@growi/app:build: Error: Unknown font format
@growi/app:build: Webpack Bundle Analyzer saved report to /opt/growi/apps/app/.next/analyze/client.html
```

Google で検索してみてもそれらしい記事を見つけられませんでした。

本家のディスカッションに質問を投稿してみました。

- [ビルド中 webpack で Error: Unknown font format が出て Material Icons 等が表示されない #8779](https://github.com/weseek/growi/discussions/8779)

### 解決

上記投稿に回答をいただき、フォントファイルが、[Git Large File Storage (LFS)](https://git-lfs.com/) で管理されているため、正しく取得出来ていなかったことが、分かりました。

```
$ apt update && apt install git-lfs
```

を行った上、

```
$ cd /opt/growi
$ sudo git lfs pull
```

でフォントファイルがビルドできるようになりました。

#### 参考

- [Git LFS のリポジトリをcloneやpullしたときにどうなるのか #Git - Qiita](https://qiita.com/yuki2006/items/6893d7fcc1014c5cfb41)

> Git LFSを導入してなくても clone/pullをすることができ、しかもエラーになりません。
>
> 当該のファイルは メタデータのような参照の情報が入っている小さなファイルになっています。
ビルド時に初めて気づくことになったり、はたまた本番運用中に気づくということにもなりかねません。
なので周知力が必要です。

## 最後の手段、公式の Docker イメージから抽出してしまえ

とりあえず、早く試験環境を構築して、他の社員に評価してもらいたいので原因追究の時間が惜しい。素直に公式の Docker イメージから動く環境を抽出することにしました。

- [Dockerイメージに含まれているデータを抽出する](https://zenn.dev/ynug/articles/docker-image-extraction)

の方法も試そうとしたのですが、/opt/growi 以下が複数のレイヤーに分かれているようなので断念しました。

- [起動中の docker コンテナのシェルに入る #Docker - Qiita](https://qiita.com/sekizo/items/27cc9b406332afc674f6)

の方法を使用して、コンテナのシェルに入り、/opt/growi をまとめてアーカイブし、ssh （クライアント）をインストールしてから、Ubuntu に scp で送りました。

それを展開し、起動したところ、無事に正しいレイアウトで表示することができました。

![good_layout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36738/85e651d5-b314-5318-1472-1b13f4ee89c0.png)

## Nginx のプロキシを通すとビルトインのエディタにソースが表示されない…

:::note warn
追試したところ、この件、再現しませんでした。設定ファイルを分けたりするとおかしなことになるのかもしれません。
:::

[Redmine](https://www.redmine.org/) の構築経験でスクリプト系のサービスは、リバースプロキシ通さないと実用に耐えないという意識があるし、URL にポート番号が含まれるのはあまり好きではないので速やかに Nginx プロキシを公式の[リバースプロキシの設定例](https://docs.growi.org/ja/admin-guide/getting-started/ubuntu-server.html#%E3%83%AA%E3%83%8F%E3%82%99%E3%83%BC%E3%82%B9%E3%83%95%E3%82%9A%E3%83%AD%E3%82%AD%E3%82%B7%E3%81%AE%E8%A8%AD%E5%AE%9A)に準じて設定しました。

ところが、直接アクセスしていた時には問題がなかったのに Nginx を通じてアクセスすると編集の際にビルトインエディタにソースが表示されません。

:::note info
その前に js やら　css が取得できない事象があったのですが、それは、先ほどのページにはなく、Nginx のパッケージに含まれていた設定ファイルの以下の行を残していたせいでした。

```
try_files $uri $uri/ =404;
```
:::

ブラウザの開発者ツールで覗いてみると Web socket がうまく動いていないようでした。

公式の説明では、

```nginx
    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;
    }
```

のように `proxy_pass` 以外の設定も `location /` の中に書かれていますが、

- [NginxのリバースプロキシでWebソケットを通す際の設定 #nginx - Qiita](https://qiita.com/YuukiMiyoshi/items/d56d99be7fb8f69a751b)

に

```nginx
server {
    listen 80;
    server_name jupyter.sample.domain;

    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header Upgrade $http_upgrade; 
    proxy_set_header Connection $connection_upgrade;

    location / {
        proxy_pass   http://jupyterlab:8888;
    }
}
```

とあるように `proxy_pass` 以外の設定を `location /` の外に追い出すと通常の動作に戻りました。

## draw.io をオンプレミス環境で連携できるようにサービスを立てましたが…

デフォルトの apt リポジトリにある tomcat9 は、OpenJDK 17 では起動出来ませんでした。（起動スクリプトを書き換えて無理やり動かす例も web にありましたが…）

そこで tomcat 10 のバイナリパッケージを取得して、/opt に展開し、起動させました。

- [Ubuntu 22.04 LTS : Tomcat 10 インストール : Server World](https://www.server-world.info/query?os=Ubuntu_22.04&p=java&f=4)

それから、draw.io の 最新の war ファイルを [Release v24.3.1 · jgraph/drawio](https://github.com/jgraph/drawio/releases/tag/v24.3.1)から、取得し、tomcat の `webapps` ディレクトリに Deploy しました。これで `http://host:8080/draw/` でアクセスできるようになりました。

そして、GROWI の起動スクリプトを次のように書き換えました。（環境変数 `DRAWIO_URI` を追加）

```diff
12c12,13
< ELASTICSEARCH_URI=http://localhost:9200/growi
---
> ELASTICSEARCH_URI=http://localhost:9200/growi\
> DRAWIO_URI=http://host:8080/draw/

```

:::note warn
環境変数 `DRAWIO_URI` には、サーバーではなくクライアントからアクセスできるアドレスを設定する必要があります。
:::

GROWI を再起動し、ブラウザの更新を行うと編集画面で埋め込み draw.io が起動し、結果が挿入できました。

ですが、プレビューや `View` モードで図形が表示されません。

ブラウザの開発者ツールでネットワークのログを見てみると draw.io の図を表示する `viewer.min.js` が 404 で見つからない状態になっています。よく見ると URL がお菓子もといおかしい。

`http://host:8080/draw/js/viewer.min.js` でなければならないはずなのに `http://host:8080/js/viewer.min.js` と `draw` のパスが抜けてしまっています。

これは、バグであると思われるので公式に issue を投稿しました。

- [DRAWIO_URI がサーバールートでない場合、viewer.min.js の URL が正しく生成されず draw.io の図が表示できない #8769](https://github.com/weseek/growi/issues/8769)

とりあえずは、無理やり実行環境の javascript を修正して動くようにしました。今まで同じ目に遭った方、いなかったのかな…

## draw.io 立てたなら PlantUML もね…

plantuml の war ファイルは、[plantuml-server](https://github.com/plantuml/plantuml-server) の [Release v1.2024.4](https://github.com/plantuml/plantuml-server/releases/tag/v1.2024.4) から、`plantuml-jsp-v1.2024.4.war` をダウンロードし、`plantuml.war` にリネームし、tomcat の `webapps` ディレクトリに Deploy しました。これで `http://host:8080/plantuml/` でアクセスできるようになりました。

draw.io と同様、環境変数 `PLANTUML_URI` をGROWI の起動スクリプトに追加しました。

```diff
13c13,14
< DRAWIO_URI=http://host:8080/draw/
---
> DRAWIO_URI=http://host:8080/draw/\
> PLANTUML_URI=http://host:8080/plantuml/
```

- [環境変数 | GROWI Docs](https://docs.growi.org/ja/admin-guide/admin-cookbook/env-vars.html)

こちらの方は、GROWI の方で特に問題は起きませんでしたが、ちゃんと tomcat10 で動く war ファイルを探すのに手間取りました :sweat_smile: 

### (2024/5/15新規) PlantUML Server で日本語の文字化け

:::note warn
これは、GROWI では問題ありません。（SVGで取得するため）
PlantUML Server に直接アクセスして、PNG で生成する場合の話です。
:::

- [PlantUMLで日本語が豆腐になった(解決ずみ) - /home/jiro4989](https://scrapbox.io/jiro4989/PlantUML%E3%81%A7%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%8C%E8%B1%86%E8%85%90%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%9F(%E8%A7%A3%E6%B1%BA%E3%81%9A%E3%81%BF))

上記ページが、解決策となりました。

まず、対応する日本語フォントを入れます。

```
$ sudo apt install fonts-noto-cjk fonts-noto-cjk-extra
```

それから、以下のようにフォントを設定してやります。

```
@startuml
skinparam {
   FontName Noto Sans CJK JP Regular
}

Bob -> Alice : ほげ
@enduml
```

私の環境では、`defaultFontName` ではなく `FontName` でないとうまくいきませんでした。

ちなみに `{}` 内が一つのパラメータしかない場合、

```
skinparam FontName Noto Sans CJK JP Regular
```

のように一行で書いても大丈夫でした。

## (2024/5/3新規)インターネットネット接続がないと Emoji Mart の絵文字が表示できない

インターネットネット接続がないと下のスクリーンショットのように Emoji Mart の絵文字が表示できませんでした。

![emoji_mart_bad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36738/a0edfc13-a6e4-3ad7-3d70-04cceb01735a.png)

絵文字の挿入と挿入後の絵文字のプレビュー可能です。

開発者ツールで要素を調査すると以下のように background-image の URL が、[UNPKG](https://unpkg.com/) の URL になっているのが、直接の原因です。

```css
background-image: url("https://unpkg.com/emoji-datasource-apple@5.0.1/img/apple/sheets-256/64.png")
```

部品の仕様として対処可能か分かりませんが、GROWI に Issue を投稿しました。

- [インターネット接続がない環境では、Emoji Mart の絵文字が表示できない。 · Issue #8788 · weseek/growi](https://github.com/weseek/growi/issues/8788)

なお、使われている部品は、[panta82/emoji-mart: Emoji picker 👊🏼with keyboard accessibility](https://github.com/panta82/emoji-mart) の様です。

 [emoji-mart/src/utils/shared-default-props.js at master · panta82/emoji-mart](https://github.com/panta82/emoji-mart/blob/master/src/utils/shared-default-props.js) で定義されている

```javascript
const EmojiDefaultProps = {
  ...
  backgroundImageFn: (set, sheetSize) =>
    `https://unpkg.com/emoji-datasource-${set}@${EMOJI_DATASOURCE_VERSION}/img/${set}/sheets-256/${sheetSize}.png`,
}
```

の `backgroundImageFn` を上書きできれば、修正できそうな気はします。

## 参考文献（まとめ）

- [OSS開発wikiツールのGROWI | 快適な情報共有を、全ての人へ](https://growi.org/ja/)
- [Ubuntu Server | GROWI Docs](https://docs.growi.org/ja/admin-guide/getting-started/ubuntu-server.html)
- [docker-compose | GROWI Docs](https://docs.growi.org/ja/admin-guide/getting-started/docker-compose.html)
- [Ubuntu 22.04でGROWI 6.1.0を動かしてみる](https://zenn.dev/de_teiu_tkg/articles/70a8d5815f520b)
- [環境変数 | GROWI Docs](https://docs.growi.org/ja/admin-guide/admin-cookbook/env-vars.html)
- [オフライン環境のUbuntuでapt install #Docker - Qiita](https://qiita.com/tetrar124/items/40fd9b1ff5f0b015a6da)
- [オフライン環境にaptでパッケージをインストールする - iyuniY’s blog](https://iyuniy.hatenablog.com/entry/2020/05/06/223940)
- [NginxのリバースプロキシでWebソケットを通す際の設定 #nginx - Qiita](https://qiita.com/YuukiMiyoshi/items/d56d99be7fb8f69a751b)
- [Git LFS のリポジトリをcloneやpullしたときにどうなるのか #Git - Qiita](https://qiita.com/yuki2006/items/6893d7fcc1014c5cfb41)
- [起動中の docker コンテナのシェルに入る #Docker - Qiita](https://qiita.com/sekizo/items/27cc9b406332afc674f6)
- [Dockerイメージに含まれているデータを抽出する](https://zenn.dev/ynug/articles/docker-image-extraction)