Ubuntu Weekly Recipe

第704回高機能でMarkdownや作図もサポートするWiki.js

Wiki.jsはNode.jsベースのWikiシステムです。モダンな作りとスタイリッシュなデザインで、⁠とりあえずWikiだけあれば良い」という用途には最善な選択肢のひとつでしょう。今回はそんなWiki.jsをUbuntuにデプロイしてみます。

あなたのWikiはどこから?

一般的に「Wiki(ウィキ⁠⁠」と言えばWikipediaを暗黙的に意味することが多い昨今の状況ですが、本連載の読者ならおそらく誰でもご存知のように、現在ではウィキソフトウェアで動いている、ウェブブラウザーで複数のユーザーが共同で編集可能なコンテンツ管理システムの総称です。

生のHTMLを書くのに疲れた人にとって、Wikiの「人に優しいマークアップ言語[1]⁠」は魅力的に映り、現在では非常に多くの環境で様々なWikiが活用されています。その最も成功した例が、Wikipediaを支えているMediaWikiでしょう。

他にもPerlで作られたYukiWikiPHPで作られたPukiWikiRubyで作られたHikiPythonで作られたMoinMoinデータベース不要なDokuWikiなど多種多様なウィキソフトウェアが登場しました。これらの中にははじめて触ったWikiや、実際に運用してみたWikiなどいろいろあるのではないでしょうか[2]⁠。

最近ではGitHub・GitLab・Redmineなどのプロジェクト管理・チケット管理システムは当然のようにWiki機能を備えるようになり、それらを使うことのほうが多くなったかもしれません。これはWiki自身がブログなどより、多人数で執筆するナレッジベースとしての使い方に向いているのが理由でしょう。

結果的にウィキソフトウェアを単体でインストールすることは、一時期に比べて減ってきました。それでも「プロジェクト管理システムほどではないものの、軽く共有できる仕組みはほしい」という要望自体はなくなったわけではありません。そんなときの選択肢として、ここ数年で主力に躍り出てきたのが今回紹介するWiki.jsです。

Wiki.jsの主な機能は次のとおりです。

  • Markdownエディター・WYSIWYGエディター・HTMLエディターのサポート
  • gitリポジトリやクラウド上にデータを保存可能
  • 多種多様な認証システムのサポート
  • 柔軟なアクセス管理機能
  • 図の作成・埋め込み
  • 画像などのメディアコンテンツのアップロードにも対応
  • モバイルデバイスでも使いやすいUI

今風のWikiなら必要な機能は一通り備わっていると思って問題ありません。またアカウント認証・アクセス管理の機能が充実しているのも特色のひとつです。

ちなみにWiki.jsは実質、ほぼ一人で開発・維持している状態です。業務で利用したり継続的な開発を期待するなら、金銭的にサポートすることもぜひ検討してみてください。

Wiki.jsのデプロイ

それではWiki.jsをデプロイしていきましょう。Wiki.jsにはレガシーな1.0系、安定版の2.0系、現在開発中の3.0系が存在します。このうち3.0系は大幅な改良が予定されているものの、現時点ではまだコードが公開されていません[3]⁠。よって、当面は2.0系を使うことになります。

公式ドキュメントのRequirementsには、システム要件として次のような記載があります。

  • 2コア以上のCPUを推奨
  • 少なくとも1GBのRAM(Wiki.js自身は通常は70MBぐらいしか使わないが、たまにより多く使うことも)
  • ストレージ容量はコンテンツ次第、Wiki.js自身は数MB程度使用
  • アップデートや各種アドオンのインストールのためのインターネットへのアクセス(ただしインターネットから隔離されていても利用は可能)
  • アクセス可能なドメインかIPアドレス(サブフォルダーを使うのは不可、ただしリバースプロキシーを経由すれば可)

あとはSQLデータベース(一般的なSQLデータベースならどれでも良いが推奨はPostgreSQL 9.5以降⁠⁠、Node.js 10以降が必要になります。ウェブサーバーは必須ではありません。またDockerによるデプロイ等もサポートしています。

最後の要件はhttps://foo.examle.com/wikijs/みたいな使い方は、Wiki.js単体ではサポートしないということです。もちろん、Nginxなどリバースプロキシーを前段に用意すれば実現可能です。

公式ドキュメントのUbuntuのインストール方法ではDockerを使ったインストールを紹介しているものの、Dockerによるインストール方法のほうにdocker-compose.yamlの例が掲載されているのでそれを使うほうがかんたんです。

DockerそのものはDocker公式のリポジトリのそれでも、Ubuntuのリポジトリにあるdocker.ioパッケージでもかまいません。

他にもサービスごとにサーバーを分離したいのであれば、システムコンテナーであるLXDを使って、Ubuntuインスタンスを作るという手もあります。LXDの使い方は第521回の入門システムコンテナマネージャーLXD 3.0にまとめていますが、2ページ目のsudo lxd initさえ実施してしまえば、あとは次の方法でWiki.js用のシステムコンテナーを構築できます。

$ lxc launch ubuntu:20.04 wikijs
$ lxc exec wikijs -- sh -c 'apt update && apt full-upgrade -y && apt autoremove -y'
$ lxc config set wikijs security.privileged true
$ lxc config set wikijs security.nesting true
$ lxc restart wikijs
$ lxc shell wikijs
# sudo -i -u ubuntu

システムコンテナーの中でさらにdocker-composeを使うために、特権コンテナーかつcgroupのnestingを有効化しています[4]⁠。

もちろん、上記手順はスキップして、直接UbuntuにDocker環境をインストールしてもかまいません。

ここからは、LXDを使ったかどうかに関係なく共通の手順となります。まずは必要なパッケージをインストールして、docker-compose.ymlファイルを置く場所を決めます。これはどこでもかまいません。

$ sudo apt install -y docker.io docker-compose
$ sudo mkdir /etc/wikijs && cd /etc/wikijs

次に例の内容をそのままにdocker-compose.ymlファイルを作成します。

$ cat <<'EOF' | sudo tee docker-compose.yml
version: "3"
services:

  db:
    image: postgres:11-alpine
    environment:
      POSTGRES_DB: wiki
      POSTGRES_PASSWORD: wikijsrocks
      POSTGRES_USER: wikijs
    logging:
      driver: "none"
    restart: unless-stopped
    volumes:
      - db-data:/var/lib/postgresql/data

  wiki:
    image: requarks/wiki:2
    depends_on:
      - db
    environment:
      DB_TYPE: postgres
      DB_HOST: db
      DB_PORT: 5432
      DB_USER: wikijs
      DB_PASS: wikijsrocks
      DB_NAME: wiki
    restart: unless-stopped
    ports:
      - "80:3000"

volumes:
  db-data:
EOF

最後にデータベースのパスワードを変更して準備完了です。

$ sudo sed -i "s,wikijsrocks,$(openssl rand -base64 32)," docker-compose.yml

本来であればこのパスワード情報はsecretsを使うのが正しいやり方だと思うのですが、wikiコンテナー側がファイルからの設定ファイルの読み出しに対応していないようなので、ここではもう直接パスワードをYAMLファイルに書いてしまっています。気になるようなら、requaraks/wiki:2イメージではなく、自分でイメージを作ってしまっても良いでしょう。

環境変数に関しては公式ドキュメントのDockerのページに記載があります。基本的にその設定のままで良いはずです。ただしもしLet’s EncryptなどでWiki.js用にサーバー証明書の取得まで行いたいのであれば、SSL_ACTIVELETSENCRYPT_DOMAIN等の設定が必要になります。今回は前段のNginxでこれらを行うこととし、Wiki.jsそのものは80番ポートでのみ待ち受けることにします。

docker-compose.ymlports80:3000となっているため、ホストの80番ポートが使われることになります。このあたりもホストの状況に合わせて変更してください。特にリバースプロキシーを使う場合は、- 127.0.0.1:3000:3000のようにローカルループバックの3000番ポートに限定したほうが良いでしょう[5]⁠。これにより余計なサービスがインターネットに公開されなくなります。LXDコンテナーに閉じ込める場合、この80番ポートもLXDコンテナー内部の話となり、特に変更せずにWiki.jsだけが使えるようになるので便利です。

docker-compose.ymlの準備ができたら、インスタンスを立ち上げます。

$ sudo docker-compose pull
$ sudo docker-compose up -d
$ sudo docker-compose ps
    Name                   Command               State                       Ports
-------------------------------------------------------------------------------------------------------
wikijs_db_1     docker-entrypoint.sh postgres    Up      5432/tcp
wikijs_wiki_1   docker-entrypoint.sh node  ...   Up      0.0.0.0:80->3000/tcp,:::80->3000/tcp, 3443/tcp

オリジナルのdocker-compose.ymlのままだと、すべてのネットワークインターフェースの80番ポートをlistenします。つまり、すでに80番ポートを誰かが使っていたら起動に失敗しますし、起動に成功した場合はホストマシンの外からアクセスできるようになる旨、注意してください。ちなみに3443はHTTPS経由の接続です。今回は使わないため、Dockerのネットワーク内部にのみ公開されています。

この状態になったらhttp://サーバーのアドレス/でWiki.jsにアクセスできます。ただし、本来はサーバー証明書とTLSを用いて経路を暗号化することが望まれます。そこでリバースプロキシーの設定も紹介しておきましょう[6]⁠。

リバースプロキシーの設定

具体的には第700回セルフホスト可能なチャット・タスク管理サービスMattermostで参照したMattermostの設定を流用するのでもかまいません。ここではよりシンプルなNginx向けの設定だけ紹介しておきます。

$ cat <<'EOF' | sudo tee /etc/nginx/sites-available/wikijs
upstream backend_wikijs {
   server 10.4.206.95:80;
   keepalive 32;
}

server {
  listen 80;
  listen [::]:80;
  server_name wiki.example.org;
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name wiki.example.org;

  http2_push_preload on; # Enable HTTP/2 Server Push

  ssl on;
  ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:50m;
  ssl_session_tickets off;

  # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
  add_header Strict-Transport-Security max-age=15768000;

  location / {
      client_max_body_size 50M;
      proxy_set_header Connection "upgrade";
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Frame-Options SAMEORIGIN;
      proxy_http_version 1.1;
      proxy_pass http://backend_wikijs;
  }
}
EOF

ここではドメイン名をwiki.example.orgと想定しています。ドメイン名はserver_namessl_certificate/ssl_certificate_keyなどの設定で必要になります。またbackend_wikijsのIPアドレスは環境に合わせて変更してください。ホストに直接インストールしているなら、⁠127.0.0.1」で問題ないでしょう。

また、client_max_body_sizeを大きめに設定しておかないと、ファイルをアップロードしたときにエラーになるので注意しましょう。

設定を有効化し、Nginxをリロードします。

$ sudo ln -s /etc/nginx/sites-available/wikijs /etc/nginx/sites-enabled/
$ sudo systemctl reload nginx.service

あとは指定したドメイン名にアクセスすれば準備完了です。

初回ログイン時の設定

初回ログイン時には管理者アカウントの作成とトップページの作成が求められます。よって初回アクセスだけは、インターネットに公開されていない状態で実施しても良いかもしれません。いずれにせよサービスを立ち上げたらなるべくはやく実施しましょう。

図1 管理者アカウントの名前は「Administrator」となる
図1
図2 ログイン画面はやたらとおしゃれな感じ
図2
図3 まずはホームページの作成が求められる
図3
図4 エディターの選択画面
図4

2.0系では、エディターの種類として「Markdown」⁠Code(HTMLを直接入力する方法⁠⁠Visual Editor(WYSIWYGな編集画面⁠⁠」のいずれかを選択できます。ちなみに、一度そのページのエディターを選択したら、基本的に同じエディターを使い続けることになります。⁠ページを変換」機能で他のエディターに切り替えることは可能ですが、その場合は内容もエディターの形式に合わせて再変換されるため、元データがまったく同じ状態では切り替わらないことに注意してください。

また、選択できない項目はいずれも将来的に実装される予定のものです。

図5 ページのプロパティの設定
図5

ページ作成時はまずプロパティを設定します。ページのタイトル、パスあたりが最低限必要なものです。⁠SCHEDULING(予約⁠⁠」では、ページの公開・非公開や公開開始・終了日時を設定できます。初期設定では誰でも閲覧可能な状態になっています。スクリプトやスタイルでJavaScriptやCSSを埋め込むことが可能です。これらの設定は編集画面の「ページ」を選択することであとからでも変更できます。

パスの先頭にある「en」は英語版を意味します。UIのロケールを日本語にすると、ここが「ja」になり日本語向けのホームページを再作成する必要がある点に注意してください。とりあえず現時点では空のページを作ってしまうのが良いでしょう。

図6 Markdownのエディター
図6

Markdownのエディターにはリアルタイムのプレビュー画面がついてきます。これでどんなふうにレンダリングされるかがわかりやすく、初心者にも優しい作りになっています。

左端にあるツールバーは拡張機能の導入などで有効化されることもあります。⁠画像の挿入」からは画像に関わらず任意のファイルをアップロードできます。ただし拡張子.md.txt.htmlはページ用に予約されているため、ファイルリンクを作成してもWiki.js内部のページとして判定されてしまいます。これらのファイルをアップロードしたい場合は、適宜拡張子を変更してください。

特に強力なのが下から3番目の「図の挿入」です。これを選択すると「作図ツール」が起動し、それをMarkdownの中に埋め込めるというものです。

図7 Wiki.jsに同梱の作図ツール
図7

この作図ツールはかなり強力で、diagrams.net(draw.io)のサイトがそのまま取り込まれています。これはdiagrams.netのEmbed modeを利用しているらしく、iframeの中にdiagrams.netのサイトが表示されている点に注意が必要です。

図8 保存するとページが生成される
図8

画面上部の「CREATE」ボタンを押すと、ページが生成されます。初回に作成する画面はいわゆる「ホームページ」であり、サイトにアクセスしたときのトップページとなります。試しにプライベートウィンドウか何かで同じURLにアクセスしてみると、ログインしなくても閲覧できることがわかるでしょう。

システム設定でできること

ホームページを作ったら、システム設定に入りましょう。

図9 管理者権限でアクセスできるシステム設定
図9

かなり設定項目が多いため、まずは一通り眺めておくと良いでしょう。

図10 Localeで言語の設定を行う
図10

UIを日本語化したい場合は、⁠Locale」から日本語ロケールの設定をダウンロードし、状態を変更します。図の真ん中にある「Multilingual Namespaces」は、サイトを多言語対応にする場合に使う機能です。つまり/en/ページ名というパスで英語のページが作られたら、/ja/ページ名で日本語のページを作成できるというものです。これをオンにすると、⁠Active Namespaces」でチェックの入っているロケールが切り替えられるようになります。またパスからロケールを省いた場合は、⁠Locale Settings」で選ばれたロケールが設定されます。

初回ログイン時は英語ロケール設定で/en/homeというページを作成しました。ここで日本語ロケールに切り替えると、ホームページは/ja/homeとなり「ホームページが未作成」ということで、再度初回ログイン時と同じようにホームページの作成が求められます。

もし英語ロケールでページを作成済みなら、一度「Multilingual Namespaces」を有効化して、英語と日本語をの双方をチェックしておくと、最初に作成した英語ロケールのページもアクセスできて、なおかつページ移動も簡単になります。そのあたりは用途に合わせてどうするか決めると良いでしょう。

図11 ローカル認証の設定
図11

Wiki.jsは特に設定していないとローカルのデータベースを利用した認証が使われます。つまりアカウントはすべて自分で作成し、データベースに保存されます。設定の「認証」にある「Local」を選択すると、このローカル認証の設定を変更可能です。

たとえばログイン画面に「アカウントの作成」リンクを追加したいのであれば「自己登録を許可する」にチェックをいれてください。またユーザーやグループは別途設定画面から管理可能です。

図12 追加できる認証の候補
図12

認証設定の「認証方式の追加」を選択すると、追加できる認証方式がリストアップされます。上記の図にあるように様々な認証方式に対応しています。これらを使うためには、APIキーの取得などサービスにあわせてひと手間必要ではあるものの、UIがきちんとしているためそこまで困ることはないでしょう。詳細は公式ドキュメントの認証のページも参照してください。

図13 ゲストグループのパーミッション
図13

ログインせずにページにアクセスした場合は「ゲストアカウント」扱いになります。このゲストアカウントは、初期設定だと公開設定になっているページやコメントの閲覧は可能になります。必ずログインしないといけない状態にしたい場合は、ゲストアカウントのパーミッションを無効化してください。

具体的には設定の「グループ」「Guests」を選択し、⁠PERMISSIONS」のチェックボックスをすべて外します。⁠UPDATE GROUP」ボタンを押せば反映完了です。プライベートウィンドウなどからhttps://ホスト名/ja/homeなどにアクセスすると、ログイン画面にリダイレクトされることがわかります。

検索については、初期設定の状態はほぼ使いものになりません。設定の検索エンジンから事前準備なく使えるのは「Database - Basic」「Database - PostgreSQL」だけです。どちらも日本語検索には非対応となっています。よってクラウドの検索エンジンなり、Elasticsearchを自前でデプロイするなどの対応が必要になります。

図14 検索エンジンの選択肢
図14

またバックアップについては、PostgreSQLを使っているならデータベースをダンプするだけです。バックアップするスクリプトさえ作ってしまえば、定期的なバックアップは第702回の方法をそのまま流用できます。

このようにWiki.jsは構築も運用も簡単なので、⁠とりあえずみんなで編集できるWikiっぽいものがほしい」と思ったときの有力な候補のひとつになるのではないでしょうか。

おすすめ記事

記事・ニュース一覧