このブログは Ghost というブログシステムによって稼働しているが、遙か昔、正式リリース前の v0.1 系 の時に入れたままで、シンプルなブログシステムとしては良く動いていたのもあってずっとアップデートをサボっていた。
しかし、リンクのついた画像を配置した場合に AMP 用のページで画像が表示されない問題に気づき、これは実はテンプレートの問題だったかもしれないのだが、この機会に最新版までアップデートしてみることにした。すんなり行ったのでやり方を記録しておく。
なお、基本的には公式の方法に従っている。まず v0.1 系から v1 系に移行し、それから v3 系に移行した。

なお、CJK Writes Matter という煽りに釣られて来た読者は感想の項を読めば良いかもしれない。

前提

現在、Ghost が /app/ghost/ にインストールされているものとし、移行後も同じディレクトリで動かすものとする。APIの変更により、テーマも移行するためには手動でテーマのファイルを修正する必要があるが、今回はテーマの移行は諦めることにする。
インストールが単一ディレクトリでの操作に完結する local install を行う。これはデータベースに sqlite を使い、nginx でのリバースプロキシを(自動で)設定しない。local でない通常のインストールを行えば、MySQL や nginx の設定を行ってくれるようだが、諸々 [1] の理由で今回は local install することにした。

v0.1 系から v.1 系への移行

v1 以降は(少なくとも現行バージョンである v3.22.1 までは)同じ方法で移行できるが、v0.1 から v1 への移行のみ異なった方法をとらなければならない。そこで、まずは v1 へ移行する。v1 への移行は、基本的には v1 を新規インストールし、既存のコンテンツをインポートするという手順になる。

  1. データをバックアップする。/app/ghost を丸ごとどこかのディレクトリにコピーしておく。例えば rsync -avz /app/ghost /tmp/backup/ghost

  2. データをエクスポートする。Web 上の管理画面の Labs メニューから、Export your content 機能を使い、全ての記事データを JSON ファイルとしてエクスポートする。

  3. Ghost を停止する。サーバーにログインし、Ghost のプロセスを停止する。当然ながらこの作業は Ghost をどうやって起動しているかによって異なる。僕はこれまで Ghost を supervisord を使って永続化していたので、supervisorctl stop ghost とした。

  4. 元のディレクトリをリネームし、同名の空のディレクトリを作る。(別のディレクトリに移行するなら、単純に新規ディレクトリを作っても良い)

$ mv /app/ghost /app/ghost.bak
$ mkdir /app/ghost
  1. Node v10系 をインストールする。Ghost v1 がサポートする最大のバージョンが Node v10 であるため。(v12 を使っていた場合、ghost-cli からサポート外のバージョンであると怒られる。 --force をつけて実行できそうだが、未確認)。Node v8 もサポートしていた気がするが、この後説明する ghost-cli が依存しているモジュールが v8 では動かないので、v10 を入れた方が良い。

  2. ghost-cli をインストールする。Ghost v1 より、Ghost のインストールや起動終了を管理する ghost-cli を通してインストールするのが推奨されているようなので、このコマンドをグローバルにインストールする。

$ npm install -g ghost-cli
  1. Ghost v1 をインストールする。
$ cd /app/ghost $ ghost install local --v1

インストールが行われ、自動で Ghost が起動する(起動したはず)。以降、/app/ghost 内で ghost start で起動、ghost stop で停止できる。

  1. http://localhost:2368 で Ghost に接続できる。http://localhost:2368/ghost でユーザーを作成し、ログインする。

  2. 管理画面の Labs → Import content から、手順 1. でダウンロードしていた JSON をアップロードする。ユーザー情報が無いのでデフォルトユーザーとしてインポートした旨のエラーが前記事に対して表示されるが、無事にインポートされた [2]

  3. アップロードしていた画像を移行する。/content/images を単純にコピーすれば良い

$ rsync -avz /app/ghost.bak/content/images/* /app/ghost.bak/content/images/ 

v1 系から v3 系への移行

Ghost v1 系以降からの以降は簡単で、ghost update コマンドを実行すれば良い。Ghost v3 では、 Node v12 が推奨されているので、Node もアップデートしたほうが良いだろう。

  1. Node v12 系をインストールする。

  2. Node のバージョンを変更したので、ghost-cli も再インストールする。

$ npm install -g ghost-cli
  1. Ghost v3 にアップデートする。
$ cd /app/ghost
$ ghost update

以上で移行は終わりだ。http://localhost:2368 が正常に閲覧できることを確認すれば良い。

感想

久々に Ghost をアップデートしたが、書くことに集中できるシンプルなブログシステムというコンセプトはぶれないまま、足りなかった機能が拡充されていて良かった。特にデフォルトテーマの Casper は、1つの記事を読ませるという点ではシンプルで見やすく、集中できて良いものの、関連記事(ジャンルごととか)への導線が弱いなと感じていたが、その部分が改善されていて良かった。あと、記事の他に、特定の日時と関連しない Page を作ることができるようになっており、ブログよりも CMS としての要素も見えるようになった。

記事編集について、以前は全記事 Markdown を使うことになっていたが、プレーンなテキストを基本として編集するように変わっており、段落ごとのコンポーネントとして Markdown や HTML を差し込んだり、Youtube の動画を埋め込んだりできるように変わっていた。人によっては、あるいはコンテンツの傾向によっては記事を書きやすくなった人もいるかもしれない。

新しい執筆画面。段落ごとにコンポーネントを挿入できる。

新しい執筆画面。段落ごとにコンポーネントを挿入できる

ただ、プレーンテキストの編集については IME と相性が悪くてまともに使える状態ではなく、しかもこのバグは数年にわたって修正されずにいる [3] ようなので、常に Markdown のコンポーネントを差し込んで記事を書き続ける運用にしようと思う。Markdown エディタ上では IME の問題は発生しない。あと、以前は所々 Markdown の解釈がおかしいところがあったがそのあたりも修正されていた。

以前の Casper のテーマは、合字関係で不適切な CSS の指定があり、タイトルの表記が時々壊れていたのでそれを修正した Casper-ja を作っていた が、この問題も現在のテーマでは修正されているようなので良かった。後でフォントだけ Yu Gothic に変更しようかと思う。

GhostのデフォルトテーマであるCasperの日本語対応フォークを公開した
どこで https://github.com/mecab/Casper-ja で。 説明 このblogを始めた時の記事 [https://blog.misosi.ru/2015/01/31/blogzai-kai-sita/] で書いたように、このblogはGhostというブログシステムで動いている。これに書いたように、Ghostはシンプルかつ軽量で、また導入も簡単で非常によい。デフォルトのテーマであるCasperも美しいのだが、日本語の記事を書いた際に問題が起こることに気づいた。 ??? pic.twitter.com/yu0Q0dYMtL [https://t.co/yu0Q0dYM…

それにしても、Ghost のチームには CJK を使う人がいないようで、IME の問題が長らく放置されていたり [3:1]、Casper の CSS の問題を指摘した時も、「後で多言語対応については考えるからとりあえずテーマは勝手にフォークしてくれ」と言われた[4] だったりで、CJK 文化圏に対して容赦なくてつらい。文字入力・表示が常にまともに動いている恵まれた環境下で過ごしている彼らは、IME がまともな動作をしないときにどれぐらい執筆意欲を削られるかとか、文字が変な表示をするときにどれぐらい文書を読む気が削がれるかに気づいていないように思う。

特定の言語を使うと、言論プラットフォーム上での言論が制限される [5] という点で master とか slave の単語が良くないという問題より問題が大きい気がするけど、CJK 奴が IT で覇権を取れなかったから仕方ないね。CJK Writes Matter.

...と、煽り気味に書いたものの、僕は気に入らないなら見るな使うな原理主義者だし、Markdown で書いていれば問題は起こらないし、そもそも総合的には満足しているしで、使いやすいブログシステムとしてこのプロジェクトには感謝している。自分でパッチ当てれば直るしね :)

その他の小ネタ

設定ファイル

ポートとか、コンテンツのディレクトリとか、データベースについては ghost/config.development.json にあるので、必要に応じて変更すれば良い。また、予想できるように NODE_ENV=production していると、ghost/config.production.json が参照される。自分の環境では、コンテンツ等のパスが絶対パスで設定されていたが、相対パスの方が管理しやすそうに感じたので相対パスに書き換えた。Ghost のソースがバージョンごとに格納されている ghost/versions/${version}/ からの相対パスになるので、../../content/... といった指定になった。

Ghost インストール後に Node のバージョンを更新したとき

Ghost をインストールした後に、Node のバージョンを更新した場合、既にダウンロードされた Ghost の依存モジュールと不整合を起こすかもしれない。このときは、ghost update --force して、Ghost とその依存モジュールを強制的にインストールし直すと良い。

Ghost インストール後にディレトリ名を変更したとき

ghost-cli はいくつかの設定を絶対パス名で行うので、Ghost のインストール後にディレクトリの名前を変更したり移動した場合壊れる。1つは上記に書いた設定ファイルで、2つめは現在の Ghost のバージョンを指しているシンボリックリンクだ。ghost/current というシンボリックリンクは、現在利用している Ghost のバージョン、つまり ghost/versions 以下のどれかのディレクトリを指しているが、絶対パスで指定されている場合リンクが無効になるので、これを修正する必要がある。最後にテーマだ。デフォルトテーマの Casper を使っている場合、ghost/content/themes/casper は、ghost/version/${version}/content/themes/casper へのリンクになっているが、このパスも壊れたので修正する必要がある。自分でテーマを入れた場合はシンボリックリンクになるのか、ghost/content/themes 以下に実体が置かれるのかは未確認。

Systemd

また、これを機に supervisord でプロセスを永続化させていたのを、systemd でシステムサービスとして動かすようにした。Node バージョンは nvm を使って管理しているが、以下のような Unit ファイルで上手く動いている。

[Unit]
Description=Ghost Blogging System
Documentation=https://docs.ghost.org

[Service]
Type=simple
WorkingDirectory=/app/ghost
User=user
Environment="NODE_ENV=production"
Environment="NODE_VERSION=12.18.2"
ExecStart=/home/user/.nvm/nvm-exec ghost run
Restart=always

[Install]
WantedBy=multi-user.target

ghost run は Ghost をフォアグランドで起動するコマンドだ。「デバッグ用だから普通は ghost start / ghost stop を使え」とのメッセージが出るが、気にしないことにした。Type=forking にして、本来のコマンドで起動終了するようにするのが正しい気もするが、PID ファイルを作ってくれない気がするので諦めて ghost run でやるようにした。


  1. 通常のインストールは Ubuntu を必要としているが、Debian のサーバーにインストールしていたこととか、リバースプロキシは既に手動で設定していたこととか、MySQL 入れるのめんどくさかったし、これぐらいの小規模のブログなら sqlite でも別にいいかーと思ったこととか。バックアップも楽だし。 ↩︎

  2. 多くの記事をいろんなユーザーで書いていた場合、これを手動で直していくのは大変かもしれない...。でも v0.1 系はそもそもマルチユーザーに対応していただろうか...。このブログは 1 人で書いていて気にしていなかったし忘れてしまった。 ↩︎

  3. 始めて報告されたのは 2017 年 4 月の #8319。その後、同じ件が #9801#11460 と定期的に報告されているが、いずれも未解決のまま close されている。この問題は、Ghost が依存している bustle/mobilekitのバグ が根本的な原因で、こっちで議論して直せとの話になっていて、それはそれで正しいと思うのだが、2年以上にわたって解決されない上に、ブログシステムとしての基本的な機能を損なっている点で、他の類似モジュールに切り替えるとか、(mobilekit に対してはPRもいくつか寄せられているので)独自にパッチするとか、何かやり方はある気がする。なお、最近 mobilekit の方で動きがある (bustle/mobilekit #736, #738) ので近いうちに修正されるかもしれない。期待したい。 ↩︎ ↩︎

  4. まだ多言語対応やってないから後でな。というのは分かるが、そもそもこれは discretionary (/任意の, 裁量による) ligatures を、どんな言語、どんなフォントが当たるか分からない状態で使うのかというという話だったのでは...。と今になっては思う。 ↩︎

  5. Ghost のチームは、OSSでの開発だけではなく商用で SaaS としての提供も行っている。 https://ghost.org/pricing/ ↩︎