zennのローカルプレビューを開けるDocker image作成しました

公開:2020/09/18
更新:2020/10/15
3 min読了の目安(約3100字TECH技術記事

あらまし

先日zennに登録して、GitHubリポジトリ連携が面白そうだったので、
早速連携しました。

しかし、GitHubリポジトリ連携をすると、リポジトリからしかWriteできなくなります。
すなわちzenn側のWebエディタは使えない状態になって、記事を書くにはローカル環境で執筆→GitHubにPushする方法のみになります。
(このあたりは公式も認識しており、他にも書いている方もいらっしゃいました)。

[2020/10/15追記] リポジトリ連携している状態でも、Webエディタが開けるようになりました

執筆後の更新で、リポジトリ連携している状態でも、
WebエディタでMarkdownファイルのソースを確認・更新できるようになっていました。
ただし、GitHub側から更新された場合、Webエディタで編集した差分は上書きされるそうです。
(あくまで、zenn→GitHubリポジトリへ書き込みできるようになるまでのつなぎ機能とのことです)

基本的な使い方としては、GitHub連携前に書いた記事をリポジトリに移行するのが主用途になるかと思います。
(もしくは、上書きされるのを把握した上でエディタとして使用して、毎回手動でGitHub側に反映するなど)

こうなった場合、記事のプレビューをするためには公式に提供されている、zenn-cliを使用する必要があります。
zenn-cliはnpmで簡単にインストールできますが、どうせなら記事を連携しているリポジトリがあれば、簡単にプレビュー環境を再構築できるようにしたいと思いました。
そこで、zenn-cliをDockerイメージにした上で簡単に立ち上げられるようにしてみました。

Dockerイメージの作成

最初にzenn-cliインストール済みのDockerイメージを作成します。

最終的に作成したDockerfileは次のようになります。

FROM node:14.11.0-alpine3.12
RUN apk add --no-cache --update git
RUN npm init --yes
RUN npm install zenn-cli

node:14.11.0を使用する

このイメージでは、元のイメージに素のalpineではなく、node提供のイメージを使っています。
これはalpine3.12でnodeインストール→zenn-cliを使用した場合、プレビュー画面のURLを開いた際にReferenceError: url is not defined というエラーが出て、正常に表示されなかったためです。

調べたところ、Node.jsのバージョンが古いと発生するとのことだったので、nodeのバージョンが最新のイメージを使用しました。

Gitインストール

2行目でGitをインストールしているのは、zenn-cliが依存しているなんらかのパッケージがGitコマンドを実行しているためです。
Gitがないと次のようなエラーでnpm installに失敗します。

npm ERR! code ENOENT
npm ERR! syscall spawn git
npm ERR! path git
npm ERR! errno -2
npm ERR! enoent Error while executing:
npm ERR! enoent undefined ls-remote -h -t ssh://git@github.com/catnose99/markdown-it-custom-block.git
npm ERR! enoent
npm ERR! enoent
npm ERR! enoent spawn git ENOENT
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

だから、事前にGitをインストールしておく必要があったんですね。

このDockerfileで作成したイメージは、 ik11235/zenn-cli という名前でdocker hubにpushしています。

イメージを使用したDocker Composeの利用

上記のイメージを使用すれば、zenn-cliがインストール済みのマシンとして使うことができます。
しかし、zenn-cliは記事のプレビューに使用するものであり、その記事データが必要です。

プレビュー環境という特性上、コンテナ自体は常時起動 & 記事ファイルは適時編集されます。
そのため、コンテナ内にデータを事前にコピーする等の手段は、今回の用途に向いていません。
今回は、ホストマシンの当該ディレクトリをマウントするようにします。

それらを含めた環境を次のdocker-compose.ymlで作成します。

version: '3'
services:
  zenn:
    image: ik11235/zenn-cli
    volumes:
      - ./articles:/articles
      - ./books:/books
    ports:
      - "8000:8000"
    command: ["./node_modules/.bin/zenn"]

使い方

Dockerをインストール済みのマシン、リポジトリ連携しているディレクトリ上で次のように上記のdocker-compose.ymlを設置してください。

.
├── articles
│   ├── example-article01.md
│   └── example-article02.md
├── books
└── docker-compose.yml

その後、当該のディレクトリで次のコマンドを実行すればプレビューサービスが起動します。

docker-compose up

http://localhost:8000/ を開けばプレビュー画面が見えているはずです。

プレビュー画面で使用するポート番号を変更する場合は、docker-compose.ymlports の左側のポート番号を変更します。
変更したポート番号にコンテナの8000番ポートがフォワードされるようになり、ポート番号を変更できます。

終わりに

この記事を書くにあたって、実際にこのプレビュー環境を使っているのですが、特に問題なく使えてます。

ただ、どうせローカルで記事を書くなら、textlintも入れたいと記事を書いている際に思ったのですが、textlintもnpmでインストールするため、無理にDockerコンテナにしなくても良かったのではという説が自分の中で浮上しています…