Gollumで自宅用のWikiを作った話

自宅で個人用WikiをGollumで立てたのでその話を書きます。

Gollumとは

Gollum とはRuby/Sinatra製のWikiです。MediaWiki等の高機能かつ大人数で使うことを前提としているWikiと比較すると、かなりシンプルな作りなのが特徴です。 GollumはGitHub Wikiのエンジンにも使われています。 基本的にmarkdownで記述することが想定されていますが、それ以外の軽量マークアップ言語も扱えます。例えばorg-rubyのgemをインストールするだけで org-mode も (部分的に) 使うことができる様になります。 また、Wikiの版管理にはSQL等ではなくGitが使われているため、例えば直接markdownを編集して git commit する、といった編集も可能です。

今回の要求仕様

ところで個人用Wikiを立てるにあたり、要求仕様は大体以下の様な感じでした。こうしてみるとGollumの選択はなかなか良さそうですね。

  • 個人Wikiなので、ユーザ登録等の本格的な機能は不要
    • 例えばMediaWikiとかは高機能すぎる
  • ハードウェアの移行が容易な作りが良い
    • ハードウェアはRaspberry Pi + Micro SDカードを使うので、ディスク寿命はそこまで長くない (はず)
  • メンテナンスコストは最小限にしたい
    • できるだけシンプルな作りが良い
    • バージョンアップ等の手間が少ない方が良い
  • (optional) Wikiの外部から編集できると便利
    • Emacsが手に馴染んでいるので、Emacsからも編集できると良い
  • (optional) org-modeの記法も使えると良い
    • Emacsのorg-modeが手に馴染んでいるので
  • (optional) Basic認証はかけられる
    • これはApache等httpサーバでも設定可能なのでなくてもよい

こういう感じで作りました

上記の要求仕様を充たすために、以下の感じのシステムを作りました。

以下、各要素について説明していきます。

Docker

ハードウェアの移行をできるだけ容易にするためにDockerを使うことにしました。 本家のDockerイメージおよびDockerfileもありますが、Dockerイメージのbuildが古くて中にあるgollumのバージョンが古いのと、org-modeを使うために org-ruby を追加したいのでDockerfileを修正して自前でイメージをbuildしました。 以下、知っている人には当たり前なことを一応書きます。

DockerイメージのbuildはDockerfileのあるディレクトリで以下のコマンドを叩きます。

docker build -t gollum .

Docker imageの起動は以下のコマンドで行います。 --restart=unless-stopped を付けておくとマシンの再起動時等に自動でこのイメージも起動するので便利です。 また、今回はApacheでlocalhostの4567番ポートへ転送するので --network=host を付ける必要があります (解説) 。

docker run --init -d  --name gollum --restart=unless-stopped -v ${PWD}:/wiki --network=host gollum --config=config.rb --template-dir=templates

以下gollumに渡すオプションについて説明します。 まず --config=config.rb ですが、このオプションではgollumの設定を記述したファイルを指定しています。このファイルでは、例えば自作のマクロの作成やhookの設定などを行うことができます。また、 --template-dir=templates ではgollumのカスタムテンプレートを配置したディレクトリを指定しています。Gollumではテンプレートを mustache で記述します。デフォルトのテンプレートはhttps://github.com/gollum/gollum/tree/master/lib/gollum/templates%E3%81%8B%E3%82%89%E5%8F%96%E5%BE%97%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99

Apache

Dockerイメージに外部からアクセスするために、Apacheのリバースプロキシを使いました。以下、知っている人には当たり前なことを一応書きます。Apacheの設定は以下の様になります。以下のファイルを例えば /etc/apache2/sites-available/gollum.conf に保存します。今回は wiki.example.com へのアクセスをlocalhostに転送する設定をします。

<VirtualHost *:80>
  ServerName wiki.example.com:80
  RewriteEngine on
  RewriteCond %{HTTP_HOST} ^wiki\.example\.com
  RewriteRule ^/(.*)$ https://wiki.example.com/$1 [R=301,L]
</VirtualHost>

<VirtualHost *:443>
  ServerName wiki.example.com
  ProxyPreserveHost On
  ProxyRequests off
  ProxyPass / http://localhost:4567/
  ProxyPassReverse / http://localhost:4567/
</VirtualHost>

まず以下の部分HTTPでのアクセスをHTTPSへのアクセスに変更します。より具体的には、Apacheの mod_rewrite を使ってhttp://wiki.example.com/ 以下へのアクセスをHTTPの301メッセージ共にhttps://wiki.example.com/ 以下へのアクセスに書き換えます。RewriteCondで書き換え対象のURLのパターンマッチを行い、RewriteRuleで書き換えルールを正規表現で指定します。 最後の [R=301,L] の部分がフラグの部分で、ここで301メッセージでのリダイレクトであることと、これが最後の書き換えであることを指定しています。

<VirtualHost *:80>
  ServerName wiki.example.com:80
  RewriteEngine on
  RewriteCond %{HTTP_HOST} ^wiki\.example\.com
  RewriteRule ^/(.*)$ https://wiki.example.com/$1 [R=301,L]
</VirtualHost>

次にリバースプロキシの部分の設定を行います。

<VirtualHost *:443>
  ServerName wiki.example.com
  ProxyPreserveHost On
  ProxyRequests off
  ProxyPass / http://localhost:4567/
  ProxyPassReverse / http://localhost:4567/
</VirtualHost>

なお今回はLet’s encryptを使ってSSLによる暗号化を行っているので、本当はその為の設定も記述する必要がありますが、certbotが自動で設定してくれることもあり、今回は省略しています。

上記の設定を有効化するためには、以下の様に a2ensite を実行します。

sudo a2ensite gollum

GitHub

Wikiの内容のバックアップ及び外部からも内容を編集できる様にするために、GitHubのプライベートリポジトリとも同期する様にしました。同期の速度を考えると自前でGitサーバを準備する方が良いですが、前述の通りディスクがMicro SDなので、耐障害性を優先して外部に保存することにしました。基本的な動作としては以下の様になります。なおそもそも利用者が非常に限られている想定なので、ほぼ同時に二箇所で変更が行なわれた場合の動作は考慮していません。例えば git pull する前に git push するとconflictして上手く動かないはずです。

  • Wikiが更新されたとき → system("git push&") でGitHubへpushする。 git push は数秒程度時間がかかるのでバックグラウンド実行させて、ページの読み込み自体はすぐに終わる様にする。
  • GitHubが更新されたとき → GitHub Actionsで秘密のwebhookにアクセスする。このwebhookから git pull を行う。

具体的な設定は以下の様になります。

まずGollum側のhookについては、以下の設定を config.rb に追加します。ここに記述している通り、Docker上の /wiki/home_wiki に秘密鍵を置く必要があります。今回は新たに ssh-keygen で秘密鍵を作成し、このリポジトリ用に鍵を登録しました。

require 'gollum-lib'

Gollum::Hook.register(:post_commit, :hook_id) do |_, _|
  # Git push after commit
  system 'GIT_SSH_COMMAND="ssh -i /wiki/home_wiki -o IdentitiesOnly=yes -o StrictHostKeyChecking=no" git push origin master &'
end

GitHub Actionsの設定は以下の様になります。GitHubのリポジトリにpushされると https://wiki.example.com/path/to/nice/webhook へwebhookが飛ぶ様になっています。

name: Gollum Pull

on: [push]

jobs:
  pull:
    runs-on: ubuntu-latest

    steps:
    - name: Webhook for gollum pull
      uses: joelwmale/webhook-action@master
      with:
        url: https://wiki.example.com/path/to/nice/webhook

まとめ

Gollum、簡単でしょ?