Docker + VSCode で Zenn で記事を書く環境を作る
目標
Zenn で記事を執筆するために、以下のような環境を作ります。
- Zenn CLI を使って記事を管理
- VSCode で記事を執筆
- textlint を導入し、文章をレビューする
- これらを Docker で環境を構築する
完成すると、このようになります。
実際に、Hiroya-W/zenn-docsで利用し、この記事も執筆されています。
環境構築
ここではホスト側の ~/zenn-docs
をワークスペースとして環境構築を進めていくことにします。
Host$ mkdir ~/zenn-docs
Host$ cd ~/zenn-docs
node.js用のpackage.json、package-lock.jsonを生成する
以降の手順を行うために package.json
、 package-lock.json
が必要になります。
それらを生成するために node.js の環境が必要になりますが、ホスト側には node.js の環境がありません。
そこで、node.js のコンテナイメージを用いて、生成させることにします。
Dockerfile
コンテナイメージを作成するための Dockerfile
を記述します。
docker
ディレクトリを作成し、その中に Dockerfile
を作成します。
Host$ mkdir ~/zenn-docs/docker
Host$ vim ~/zenn-docs/docker/Dockerfile
FROM node:lts-alpine
USER node
WORKDIR /workspaces/zenn-docs
docker-compose.yml
用意した Dockerfile
を用いて、コンテナを起動する方法を docker-compose.yml
に記述します。
Host$ vim ~/zenn-docs/docker-compose.yml
version: '3'
services:
main:
build: ./docker
volumes:
- type: bind
source: "."
target: "/workspaces/zenn-docs"
consistency: "cached"
ホスト側のディレクトリ .
を、コンテナ内の /workspaces/zenn-docs
にマウントします。
これで、コンテナ内で作成した package.json
と package-lock.json
がホスト側に配置することが出来ます。
コンテナを起動する
コンテナを起動し、そのコンテナで /bin/ash
を実行します。
Host$ docker-compose run --rm main /bin/ash
...
/workspaces/zenn-docs $
package.json
と package-lock.json
を生成します。
/workspaces/zenn-docs $ npm init --yes && npm install
/workspaces/zenn-docs $ ls
docker docker-compose.yml package-lock.json package.json
Zenn CLI と textlint 関連のパッケージをインストールし、package.json
と package-lock.json
にパッケージ情報を追加します。
/workspaces/zenn-docs $ npm install \
zenn-cli \
textlint \
textlint-rule-preset-ja-spacing \
textlint-rule-preset-ja-technical-writing \
textlint-rule-spellcheck-tech-word
Zenn用のディレクトリ構成を作成する
articles
や books
などの Zenn 用のフォルダやファイルを生成しておきます。
/workspaces/zenn-docs $ npx zenn init
Zennの記事を執筆するためのコンテナを作成する
ここからは、Zenn CLI を用いて記事を執筆するためのコンテナイメージを作成していきます。
先程作成した package.json
や package-lock.json
を用いてコンテナを作成できるようにし、Zenn 用のディレクトリを Docker コンテナにマウントして利用できるようにします。
前準備
package.json
と package-lock.json
を Dockerfile
から扱えるように、ファイルの配置位置を docker
フォルダ内に変更しておきます。
Host$ cd ~/zenn-docs
Host$ mv package.json package-lock.json docker
また、ホスト側には node_modules
フォルダは不要なので、削除しておきます。
Host$ cd ~/zenn-docs
Host$ rm -rf node_modules
Dockerfile
まずは、先程作成した、Dockerfile
を編集します。
Host$ vim ~/zenn-docs/docker/Dockerfile
FROM node:lts-alpine
RUN apk update && \
apk --no-cache add git && \
apk --no-cache add openssh
WORKDIR /workspaces/node_app
COPY ./package.json ./package-lock.json ./
RUN npm install --no-optional && npm cache clean --force
ENV PATH /workspaces/node_app/node_modules/.bin:$PATH
USER node
WORKDIR /workspaces/zenn-docs
コンテナのビルド時に、用意した package.json
と package-lock.json
を用いてパッケージのインストールをしています。
この時、/workspaces/node_app/node_modules
にパッケージがインストールされます。
また、USER node
を指定し、 一般ユーザを利用するようにしています。
一般ユーザを利用する理由
通常、コンテナ内から作成した記事などのファイルは root
ユーザで作成されます。
しかし、ホストでは一般ユーザであることがほとんどで、一般ユーザではルートユーザで作成したファイルを編集することは出来ません。
今回利用するイメージには、ホストの一般ユーザと同じ uid と gid を持つユーザ(1000:1000)として node
ユーザが存在します。
node
ユーザとしてコンテナ内で操作をすることで、この問題を解決することが出来ます。
用意されているユーザの一覧は次のようにして確認できます。
USER node
の記述を削除した状態で、コンテナをビルドして root
ユーザとしてコンテナにアタッチしておきます。
用意されているユーザ一覧は /etc/passwd
で見ることが出来ます。
root# cat /etc/passwd
...
node:x:1000:1000:Linux User,,,:/home/node:/bin/sh
docker-compose.yml
用意した Dockerfile
を用いて、コンテナを起動する方法を docker-compose.yml
に記述します。
Host$ vim ~/zenn-docs/docker-compose.yml
version: '3'
services:
main:
build: ./docker
image: zenn-base
container_name: zenn-docs
volumes:
- type: bind
source: "."
target: "/workspaces/zenn-docs"
consistency: "cached"
ports:
- "8000:8000"
tty: true
Zenn の記事データはホスト側に置き、そのディレクトリ .
を、コンテナ内の /workspaces/zenn-docs
にマウントします。
devcontainer.json
VSCode の Remote Container 用の設定を記述します。
まずは、VSCode で ~/zenn-docs
フォルダを開きます。
コマンドパレットから、> Remote-Containers: Add Development Container Configuration Files...
、From 'docker-compose.yml'
を選択し、devcontainer.json
を生成します。
.devcontainer
フォルダに、devcontainer.json
と docker-compose.yml
ファイルが生成されます。
このうち、docker-compose.yml
ファイルは必要ないので、削除しておきます。
Host$ rm .devcontainer/docker-compose.yml
devcontainer.json
を以下のように修正します。
{
"name": "Zenn Docs",
"dockerComposeFile": [
"../docker-compose.yml",
],
"service": "main",
"workspaceFolder": "/workspaces/zenn-docs",
"settings": {
"terminal.integrated.profiles.linux": {
"ash": {
"path": "/bin/ash",
}
},
"terminal.integrated.defaultProfile.linux": "ash",
"textlint.nodePath": "/workspaces/node_app/node_modules/",
},
"extensions": [
"taichi.vscode-textlint",
"negokaz.zenn-editor"
]
}
VSCode の Remote Container で開いた際のワークスペースを workspacceFolder
に指定し、ここでは workspaces/zenn-docs
にしています。
workspaces/zenn-docs
は、Dockerfile
に書いていたように、ホスト側の Zenn の記事データをコンテナ内にマウントしたディレクトリです。
また、VSCode の拡張機能としてvscode-textlint、zenn-editorをコンテナ側にインストールします。
.textlintrc
textlint 用の設定ファイルを配置します。記述する内容は、以下のサイトからお借りしました。
host$ vim ~/zenn-docs/.textlintrc
SSH agent
コンテナ内でホストの SSH key を用いて Git が使えるようにしておきます。
これで、いつもどおり GitHub のリモートリポジトリに対して git pull
や git push
が出来るようになります。
ここでは、公式ドキュメントに従って進めていきます。
ホスト側にパッケージを導入します。
# Ubuntuの場合
Host$ sudo apt update
Host$ sudo apt install openssh-client socat
# Arch Linuxの場合
Host$ sudo pacman -Syu
Host$ sudo pacman -S openssh socat
次に、ssh-agent
をバックグラウンドで実行しておきます。
Host$ eval "$(ssh-agent -s)"
SSH key を SSH agent に追加します。
なお、ここでは、秘密鍵が id_rsa
というファイル名で生成されていることを前提としています。
Host$ ssh-add $HOME/.ssh/id_rsa
次に、~/.bash_profile
に以下を追記します。
これで、常に SSH agent が実行されるようにしておきます。
if [ -z "$SSH_AUTH_SOCK" ]; then
# Check for a currently running instance of the agent
RUNNING_AGENT="`ps -ax | grep 'ssh-agent -s' | grep -v grep | wc -l | tr -d '[:space:]'`"
if [ "$RUNNING_AGENT" = "0" ]; then
# Launch a new instance of the agent
ssh-agent -s &> $HOME/.ssh/ssh-agent
fi
eval `cat $HOME/.ssh/ssh-agent`
fi
一旦再起動した後、ssh-add -l
コマンドを実行した際に、登録した鍵が表示されていればOKです。
Host$ ssh-add -l
2048 SHA256:... /home/user/.ssh/id_rsa
コンテナをビルドしVSCodeをアタッチする
ファイルを配置出来れば、Remote Container の機能を用いて Docker コンテナをビルド・起動し、VSCode にアタッチします。
コマンドパレットから > Remote-Containers: Reopen in Container
を実行します。
VSCode にアタッチできると、左下の表示が Dev Container:Zenn Docs
に変化します。
また、ターミナルから Docker コンテナ内の環境でコマンドを叩くことが出来るようになります。
例えば、id
コマンドを実行すると、node
ユーザになっていることが分かります。
ls
すると、コンテナ内にマウントされた docker
ディレクトリや docker-compose.yml
が表示されます。
また、コンテナ内で ssh-add -l
した際に同じ鍵が表示されるか、GitHub に接続できるかもを確認しておきます。
node$ ssh-add -l
2048 SHA256:... /home/user/.ssh/id_rsa
node$ ssh -T git@github.com
Hi ...! You've successfully authenticated, but GitHub does not provide shell access.
Zenn CLIを使う
VSCode 上でターミナルから Zenn CLI を利用することが出来ます。
新しく記事を作成し、ブラウザでプレビューするには次のようにします。
node$ npx zenn new:article
node$ npx zenn preview
これで、以下の URL でプレビューを確認することが出来ます。
おわりに
今回は、Docker で Zenn CLI と textlint を実行する環境を構築し、そのコンテナを VSCode で用いて Zenn の記事を執筆する環境を構築してみました。
今回のように、textlint をローカルで実行するのではなく、GitHub Actions で実行するようにしても良いでしょう。また、VSCode の Markdown 拡張機能をインストールすると、より快適になりそうです。
記事を書きつつ、更に快適な執筆環境を整えてみたいですね。
参考文献
環境構築にあたり、参考にさせていただいた資料を記載しておきます。
Discussion