Misskeyを32bit環境(Debian i386)で動かしてみる

昨今のTwitterに関する騒動の影響で、「ポストTwitter」になりうるプラットフォームが俄に注目を集めているらしい。 その一つがセルフホスト型のプラットフォームのMisskeyであり、Twitterを踏襲したタイムラインをサポートしつつ、 Slackライクなリアクションを投稿につけることが出来たり、UIの高度なカスタマイズが出来たりするなどいくつかのユニークな特徴を有している。 また、ActivityPubによる他インスタンスとの連携に対応しており、 他のMisskeyインスタンスだけでなく、Mastodon等とも連携可能な非中央集権型のプラットフォームとなっている。

MisskeyはNode.jsで書かれているため、本来はLinux x86環境で動かすことはできないのだが、 Node.jsの非公式ビルドを用いる等、様々な工夫をすることで32bit環境でも動かすことが出来た。 今回はその記録として構築手順を書いていこうと思う。

概要

今回用いる環境はDebian 11.5 (bullseye) i386である。(ArchLinuxは公式にはx86_64しか対応していない為。) また、検証に際してはVirtualBox上のVMにて作業を行っている。

Misskeyの構築手順としてDockerを使ったインストールが推奨されているため、今回はこれに従う。

Dockerを使ったMisskey構築 | Misskey Hub

やる必要のあること

32bit環境で動かすにあたって、以下の障壁をクリアする必要がある。

  • i386用のNode.js Dockerイメージの準備
  • prebuiltバイナリを用いるパッケージへの対処
    • @tensorflow/tfjs-nodeを使わないようにする
    • sharpの依存ライブラリの手動ビルド

以下ではこれらの解決方法を解説する。

※ 構築手順のみを知りたい場合、完全な構築手順を参照

i386用のNode.js Dockerイメージの準備

Node.jsは公式でLinux x86をサポートしないため、Docker Hubの公式イメージにもlinux/386版は存在しない。 ただ、Node.jsの非公式ビルドでx86版が存在するため、 x86版Node.jsイメージを作ることは技術的には可能である。

この方法で作られたと思われる非公式のNode.jsイメージがBalena社から提供されているので、 今回はありがたくこれを使わせてもらうことにする。

balenalib/i386-node - Docker Image | Docker Hub

Misskeyの公式で使われている16.15.1-bullseyeもバッチリ存在している。 (ただし、slim版は無い模様)

@tensorflow/tfjs-nodeを使わないようにする

Misskeyはオプション機能としてNSFW画像の自動判定機能が存在し、Tensorflowを用いて実装されている。 TensorflowのNode.js用ライブラリである@tensorflow/tfjs-nodeはネイティブのlibtensorflowに依存しているのだが、 このlibtensorflowはx86をサポートしていない。 このため、今回はNSFW機能の利用を諦めて@tensorflow/tfjs-nodeのインストールを回避するようにする。

なお、Misskey自体は既に@tensorflow/tfjs-nodeをoptionalDependenciesに移動しているため、 本来はパッケージのインストール時に--ignore-optionalを指定するのみで済む筈なのだが、 実際のnsfwjsのpeerDependenciesを正しく扱っていないためこのままだと依存関係エラーで起動しなくなってしまう。

nsfwjsのpeerDependenciesに含まれているのは@tensorflow/tfjsであり、 これ自体はlibtensorflowに依存していないため、単純にdependenciesに追加すれば解決する。

sharpの依存ライブラリの手動ビルド

sharpはNode.js用の画像処理パッケージで、Misskeyではアップロードされた画像のリサイズ等に用いていると思われる。 このパッケージはネイティブのライブラリであるlibvipsに依存しており、 通常のインストールであればprebuiltバイナリが自動でダウンロード展開されるようになっているのだが、 x86の場合は手動でビルドした上でホスト上に直接インストールする必要がある。 (ビルド手順: Building libvips from source)

なお、Debianのリポジトリにもlibvips42というパッケージが存在するのだが、 こちらはバージョンが古く(これは8.10.5だが、sharpには8.11以降が必要)、 sharpのインストール時に必要なcmake関係のファイルも不足しているため、これを用いることは出来ない。

今回は、Docker上でlibvipsをビルドし、ninja installで直接インストールして使ってしまうことにした。 この際、libvipsの依存ライブラリを色々とインストールする必要があるのだが、 これに関してはDebian公式のlibvips-devおよびlibvips42の依存パッケージを参考にすることとする。

完全な構築手順

以下では、Misskey Hubで説明されていることも含めて一通りの構築手順を説明する。

パッケージの更新

まず、サーバーのパッケージをすべて最新にしておく(念の為)。

1$ sudo apt -y update
2$ sudo apt -y upgrade
3$ reboot

必要なツールの準備

Git, Docker等のツールが必要になるのでインストールする。

1$ sudo apt -y install git docker.io docker-compose

Misskeyのクローン

Misskeyは常にソースからビルドする必要がある。 このため、まずはGitHubからソースコードをクローンする。

1$ git clone -b master https://github.com/misskey-dev/misskey.git
2$ cd misskey

設定

.config内に設定ファイルを作る必要があるため、まずはサンプルファイルをコピーする。

1$ cp .config/example.yml .config/default.yml
2$ cp .config/docker_example.env .config/docker.env

次に、.config/default.ymlを以下のように編集する。

  • urlhttp://localhost:3000/に変更
  • dbhostdbに変更
  • redishostredisに変更

(docker.envにはDBのパスワードしか書いていないので、今回はとりあえずそのままで良い)

Dockerfileの編集

次に、イメージビルドのスクリプトファイルであるDockerfileを編集していく。

32bitのNode.jsを使う

FROMから始まる行(2箇所)を以下のように変更する。

 1-FROM node:16.15.1-bullseye AS builder
 2+FROM balenalib/i386-node:16.15.1-bullseye AS builder
 3
 4ARG NODE_ENV=production
 5
 6WORKDIR /misskey
 7
 8COPY . ./
 9
10RUN apt-get update
11-RUN apt-get install -y build-essential
12+RUN apt-get install -y build-essential git
13
14# (中略)
15
16-FROM node:16.15.1-bullseye-slim AS runner
17+# slim版はないのでbullseyeをそのまま用いる
18+FROM balenalib/i386-node:16.15.1-bullseye AS runner
19
20# (略)

gitを追加でインストールしているのは、balenalib版のbullseyeイメージにgitが含まれていないためである。 (本家では含まれている模様)

libvipsをビルド・インストールする

以下のようにDockerfileを編集する。

 1FROM balenalib/i386-node:16.15.1-bullseye AS builder
 2
 3ARG NODE_ENV=production
 4
 5WORKDIR /misskey
 6
 7COPY . ./
 8
 9+# libvipsのバージョンは、sharpで指定されているバージョンと揃える
10+# 確認先: https://github.com/lovell/sharp/blob/main/package.json#L157
11+ARG VIPS_VER=8.13.3
12
13RUN apt-get update
14-RUN apt-get install -y build-essential git
15+RUN apt-get install -y build-essential git python3
16+RUN apt-get install -y libjpeg-dev libtiff-dev libpng-dev libgif-dev \
17+ librsvg2-dev libpoppler-glib-dev gobject-introspection zlib1g-dev libfftw3-dev \
18+ liblcms2-dev libmagickcore-dev libmagickwand-dev libfreetype6-dev libpango1.0-dev \
19+ libfontconfig1-dev libglib2.0-dev libice-dev libimagequant-dev liborc-0.4-dev \
20+ libheif-dev libmatio-dev libexpat1-dev libcfitsio-dev libopenslide-dev libwebp-dev \
21+ libgsf-1-dev libgirepository1.0-dev bc meson
22+RUN curl -O -L https://github.com/libvips/libvips/releases/download/v$VIPS_VER/vips-$VIPS_VER.tar.gz \
23+ && tar xf vips-$VIPS_VER.tar.gz \
24+ && cd vips-$VIPS_VER \
25+ && meson setup build --prefix=/vips \
26+ && cd build \
27+ && ninja \
28+ && ninja test \
29+ && ninja install
30+RUN cp -r /vips/. /usr/
31RUN git submodule update --init
32RUN yarn install
33RUN yarn build
34RUN rm -rf .git
35
36FROM balenalib/i386-node:16.15.1-bullseye AS runner
37
38WORKDIR /misskey
39
40RUN apt-get update
41RUN apt-get install -y ffmpeg tini
42
43+RUN apt-get install -y libcairo2 libcfitsio9 libexif12 libexpat1 libfftw3-double3 \
44+ libfontconfig1 libgcc-s1 libgif7 libglib2.0-0 libgsf-1-114 libheif1 libimagequant0 \
45+ libjpeg62-turbo liblcms2-2 libmagickcore-6.q16-6 libmatio11 libopenexr25 \
46+ libopenslide0 liborc-0.4-0 libpango-1.0-0 libpangoft2-1.0-0 libpng16-16 \
47+ libpoppler-glib8 librsvg2-2 libstdc++6 libtiff5 libwebp6 libwebpdemux2 \
48+ libwebpmux3 zlib1g
49+
50+COPY --from=builder /vips/ /usr/
51COPY --from=builder /misskey/node_modules ./node_modules
52COPY --from=builder /misskey/built ./built
53COPY --from=builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
54COPY --from=builder /misskey/packages/backend/built ./packages/backend/built
55COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node_modules
56COPY . ./
57
58ENV NODE_ENV=production
59ENTRYPOINT ["/usr/bin/tini", "--"]
60CMD ["npm", "run", "migrateandstart"]

行数が多いが、ほとんどがlibvipsの依存関係のインストールである。 libvipsのバージョンは必要に応じて変更すること。

また、今回のDockerfileはマルチステージビルドを利用しており、 builderrunnerの2つのコンテナを用いている。 libvipsはビルド時と実行時の両方で必要であるため、両方のコンテナにインストールする必要がある。 これを実現するため、一旦/vips/以下にインストールし、 builderrunnerの両方で/vips/の内容をシステムにコピーするようにしている。 (マルチステージングビルドではファイルコピー以外でコンテナ間の連携が出来ないため)

依存パッケージの編集

tensorflowを依存関係から外すため、以下の作業を行う。

scripts/install-packages.jsの編集

tensorflowに依存しているのはpackages/backend内のモジュールであり、 これのパッケージのインストールはscripts/install-packages.jsで行われている。 このため、以下のようにしてyarn install--ignore-optionalオプションを追加する。

 1const execa = require('execa');
 2
 3(async () => {
 4  console.log('installing dependencies of packages/backend ...');
 5
 6- await execa('yarn', ['--force', 'install'], {
 7+ await execa('yarn', ['--force', 'install', '--ignore-optional'], {
 8    cwd: __dirname + '/../packages/backend',
 9    stdout: process.stdout,
10    stderr: process.stderr,
11  });

これにより、@tensorflow/tfjs-nodeがインストールされなくなる。

packages/backend/package.jsonの編集

先述の理由により@tensorflow/tfjsを明示的にインストールする必要があるため、 以下のようにpackages/backend/package.jsonを編集する。

 1{
 2  // (中略)
 3  "optionalDependencies": {
 4    "@tensorflow/tfjs-node": "3.20.0"
 5  },
 6  "dependencies": {
 7    "@bull-board/koa": "4.2.2",
 8    "@discordapp/twemoji": "14.0.2",
 9    "@elastic/elasticsearch": "7.11.0",
10    "@koa/cors": "3.1.0",
11    "@koa/multer": "3.0.0",
12    "@koa/router": "9.0.1",
13    "@peertube/http-signature": "1.7.0",
14    "@sinonjs/fake-timers": "9.1.2",
15    "@syuilo/aiscript": "0.11.1",
16+   "@tensorflow/tfjs": "3.20.0",
17    "ajv": "8.11.0",
18    "archiver": "5.3.1",
19    "autobind-decorator": "2.4.0",
20// (略)

このとき、@tensorflow/tfjs-node@tensorflow/tfjsのバージョンが一致するようにする。

イメージのビルド

ここまでの手順が完了したら、以下のコマンドで一旦イメージをビルドする。

1$ docker-compose build

ビルドが成功すると以下のような出力が出るはずである。

1Successfully built ce6afa9c901c
2Successfully tagged misskey_web:latest

もしビルドに失敗してしまった場合、変更点に誤りがないかをよく確認すること。 (特に要求されるlibvipsのバージョンは頻繁に変わる可能性があるため、きちんと書き換えられているかよく確認する。)

なお、イメージ何回かビルドし直すと不要なキャッシュが溜まっていくので、 再ビルド前に以下のコマンドでキャッシュを削除すると良い。

1$ docker image prune

サーバーの立ち上げ

ビルドに成功したら、あとはサーバーを立ち上げるだけである。

1$ docker-compose up -d

初回起動はデータベースのマイグレーションが走るため時間がかかるが、 しばらくするとサーバーが立ち上がるので、 http://localhost:3000/にアクセスして正常に動作しているかどうか確認する。