Misskeyインスタンスを建ててみた

# はじめに
この記事の通りに作ると一見完成しているようにみえますが、後から不具合きたしまくったので新しく書き直しました！以下の記事を参考にしてください！　(2023/05/10 追記)

https://qiita.com/Soli0222/items/1a8f854706528b63a8e2



レターパックで現金送れ　与謝野晶子
この無関係な2ワードに関係性を見出してしまう方は多いのではないでしょうか。

Twitterが某起業家に買収されてから度々話題に上がるMisskey.io、筆者ももれなくハマっていますが、Fediverseの本質は個々人が自由にインスタンスを持つことができ、システムやインスタンスの壁を超えてつながり合えること。

少し技術に触れている身からすると、自分でインスタンスをもつことにはとても興味があるわけです(その過程にも)。思い立ったが吉日、手をつけてやっと安定したのでその過程を備忘録的にまとめていきます。これからインスタンスを持ってみたいという方の参考になれば良いかと思います。

# 用意したもの
インスタンスを立ち上げるにあたって、用意したものを記述していきます。
* さくらVPS 石狩第1ゾーン, 仮想2Core, 1GBメモリ, SSD 50GB
* Google Cloud Strage
* ドメイン
* Mailgunアカウント
* Cloudflareアカウント

現状3人のインスタンスですので、この程度のスペックで十分動いています。

# 建てていく
早速作業を進めていきます。


## スワップ領域の設定
2GB以上のメモリを積んであれば問題ないかと思われますが、1GBですと普通にメモリが足りません。
スワップ領域の設定を行えば、全く解消できるので、やっておいた方が絶対に良いです。下記の記事にわかりやすく全部まとまっているので、参考に。

https://chatnoirlibre.com/ubuntu-22-04-lts-swap/

## Misskeyの構築
Misskey本体を構築していきます。公式のドキュメントは以下の通りです。
基本的にドキュメント通りに進めていけば問題ありませんが、設定の内容なども書きたいので、1から書いていきます。

https://misskey-hub.net/docs/install/docker.html

公式リポジトリを取得します。
```bash:bash
$ git clone -b master https://github.com/misskey-dev/misskey.git
$ cd misskey
$ git checkout master
```
　
各種設定ファイルのサンプルをコピーします。
```bash:bash
$ cp .config/docker_example.yml .config/default.yml
$ cp .config/docker_example.env .config/docker.env
$ cp ./docker-compose.yml.example ./docker-compose.yml
```

この設定ファイルを編集していきます。

``default.yml``で変更した場所は4箇所です。
DB周りは飛ばすと致命的なのでしっかりバックアップをとっておいた方がいいです。
```yml:default.yml
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Misskey configuration
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

#   ┌─────┐
#───┘ URL └─────────────────────────────────────────────────────

# Final accessible URL seen by a user.
url: https://xxx.com #ここに使うドメインを記述

...

  # Database name
  db: xxxx #任意のDBの名前を記述

  # Auth
  user: xxxxx #DBのユーザー名
  pass: xxxxx #DBのパスワード

...
```
　
続いて``docker.env``を編集していきます。
ここにはさっきの``default.yml``に書いたことと同じことを書けば大丈夫です。
```env:docker.env
# db settings
POSTGRES_PASSWORD=xxxxx #さっき設定したDBのパスワード
POSTGRES_USER=xxxxx #さっき設定したDBのユーザー名
POSTGRES_DB=xxxx #さっき設定したDBの名前
```
　
最後に``docker-compose.yml``を編集していきます。
コメントがあるところを書き換えたり増やしたりしました。手元でビルドできそうなスペックであるなら、変えなくても大丈夫かと思いますが、今回は無理そうと判断したのでイメージを取得するようにしています。

```yml:docker-compose.yml
version: "3"

services:
  web:
    image: misskey/misskey:latest #buildではなくimageを取得する
    restart: always
    links:
      - db
      - redis
#      - es
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    ports:
      - "3000:3000"
    networks:
      - internal_network
      - external_network
    volumes:
      - ./files:/misskey/files
      - ./.config:/misskey/.config:ro
    
    #追加する
    environment:
      VIRTUAL_HOST: xxx.com
      VIRTUAL_POST: 3000
      LETSENCRYPT_HOST: xxx.com
      LETSENCRYPT_EMAIL: yyy@xxx.com

  redis:
    restart: always
    image: redis:7-alpine
    networks:
      - internal_network
    volumes:
      - ./redis:/data
    healthcheck:
      test: "redis-cli ping"
      interval: 5s
      retries: 20

  db:
    restart: always
    image: postgres:15-alpine
    networks:
      - internal_network
    env_file:
      - .config/docker.env
    volumes:
      - ./db:/var/lib/postgresql/data
    healthcheck:
      test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
      interval: 5s
      retries: 20

#  es:
#    restart: always
#    image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
#    environment:
#      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
#      - "TAKE_FILE_OWNERSHIP=111"
#    networks:
#      - internal_network
#    volumes:
#      - ./elasticsearch:/usr/share/elasticsearch/data

networks:
  internal_network:
    internal: true
  external_network:
    external: true　 #新しく生やした
```
 　
自分で記事に沿ってやってたらめちゃくちゃ設定忘れてました。
nginxとmisskeyを繋ぐためのネットワークを作る必要があります。 (2023/04/12 追記)
```bash:bash
sudo docker network create external_network
```
  
これで設定は終わったので初期化と起動を行います。
さくらVPSでやったときはスムーズに行ったのですが、Macの検証環境でやったときにイメージが見つからずに怒られが発生したので、そのときは落ち着いて``docker pull``でイメージを落としましょう。
```bash:bash
$ sudo docker compose run --rm web pnpm run init
$ sudo docker compose up -d
```
　　
起動が完了したら
```bash:bash
$ curl localhost:3000
```
で確認しましょう。ちゃんとソースコードが出てきたら完了です。

## nginxの構築
リバースプロキシのためのnginxを構築していきます。ymlを書く程度ですが…
```yml:docker-compose.yml
version: "3"
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs:ro
      - /etc/nginx/vhost.d
      - /usr/share/nginx/html
    restart: always
    networks:
      - external_network


  letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: letsencrypt
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./certs:/etc/nginx/certs:rw
    volumes_from:
      - nginx-proxy
    restart: always
    networks:
      - external_network

networks:
  external_network:
    external: true
```
　
これだけで設定は終わりなので、起動しましょう。
```bash:bash
$ sudo docker compose up -d
```
起動が完了したら、もうアクセスできるはずなので、設定したドメインからアクセスして、管理者アカウントを作成しておきましょう。
アカウントの作成が済んだら、次の作業に進みます。

## CDNの設定
公式ドキュメントでも言われている通り、MisskeyはCDNの利用が推奨されています。

https://misskey-hub.net/docs/admin/cdn.html

私はCloudflareを使いましたので、これで進めていきます。

https://www.cloudflare.com/ja-jp/

アカウントを作成し、ドメインを登録したら、いくつか設定を進めていきます。

まずは、SSL/TLS→概要で暗号化モードを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287582/71c72f4e-889a-fe4e-fe84-05c375cac512.png)

次に、ルール→ページルールからページルールを作成します。
``xxx.com/api/*``のようにし、キャッシュレベルをスキップにします。リレーを行うときにすべき設定らしいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287582/4545ec96-9082-fe8f-a8b8-a614d17df099.png)

これで一旦Cloudflareの設定は終わりです。証明書関連のエラーや、リダイレクトが繰り返されたなどの問題が発生している場合は、一旦nginxを止めて、ymlファイルのLet's Encryptの設定を削除してからもう一度起動すると治るかと思います。

## オブジェクトストレージの設定
アップロードされた画像や、絵文字のデータはそのままの設定だとすべてローカルに保存されます。これだと無限にVPSの容量を圧迫するので、別にストレージを確保します。AWSやWasabiなどがありますが、別件でGCPのアカウントを持っていたので、流れでGoogle Cloud Storageを選択しました。

基本的な設定はこちらの記事が参考になりました。相互運用性のタブをクリックしたらそこで止めてください。

https://girak.net/2022/06/11/317

Misskeyで用いるにあたって、公開サーバーにする必要があります。
これに関しては公式のドキュメントがわかりやすいので、参照してください。

https://cloud.google.com/storage/docs/access-control/making-data-public?hl=ja#buckets

## オブジェクトストレージのリバースプロキシのようなもの
純粋にオブジェクトストレージのURLをMisskeyに設定しても問題ないのですが、利用するオブジェクトストレージを変更し、URLが変わったときに連合先のインスタンスではリンク切れが多発します。なので、保有しているドメインからオブジェクトストレージに対してリバースプロキシを行うことで、この先なにがあってもいいようにしておきます。

ようなもの…と書いたのはnginxに設定を追加して自鯖で処理すればよかったのですが、なぜかうまくいかず、前述したレートリミットにぶち当たってどうにもならなくなってしまったので、CloudflareのWorkersルールで強引に解決しました。(一応リクエスト数が1日10万件となっているので、おいおい自分でできるようにすべきと思います。)

CloudflareのダッシュボードからWorkersルートを選択し、Workersを管理する、サービスを作成と進み、エディタのようなものが出てきます。ここに以下のスクリプトをコピペします。
```javascript:workers
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const url = new URL(request.url);
  const bucketUrl = `https://storage.googleapis.com/YOUR_BUCKET_NAME${url.pathname}`;
  const response = await fetch(bucketUrl, request);
  return response;
}

```
``YOUR_BUCKET_NAME``はオブジェクトストレージで設定したバゲットの名前に置き換えてください。
これで保存、デプロイし、HTTPルートを追加します。任意のドメイン(筆者はmi.soli0222.comをインスタンスURL、media.soli0222.comをストレージのものとしています。*.xxx.comならCloudflareがついでに証明書もつけてくれます。)に``/*``をつけて保存すれば完了です。

GCSの画面から適当な画像ファイルをアップロードして、公開URLをコピー、ブラウザからアクセスして画像が表示されるのを確認したら、``storage.googleapis.com/YOUR_BUCKET_NAME``の部分を先ほど設定したドメインに置き換えて、同じ画像が表示されるか検証してください。されれば完了です。

## Misskeyにオブジェクトストレージを設定する
まず、GCSの設定から相互運用性を開きます。
ユーザーアカウントのHMACで鍵を作成し、アクセスキーとシークレットを取得します。

次に、Misskeyのコントロールパネルを開き、オブジェクトストレージを選択します。
BaseURLには先ほどCloudflareで設定したURL、Prefixはバゲット内のディレクトリなので、好きなように設定してください。
また、Access KeyとSecret KeyはGCSで取得したものです。
リージョンなど異なる場合がありますが、他は画像のように設定すれば完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287582/03e9eb1a-4e8f-f92f-9980-7b586ebca685.png)

ノートを作成し、正しく画像が表示されていれば完了です。

## メールサーバーの設定
最後です。メールサーバーを設定します。
これはパスワードを忘れたときや、お知らせの配信のために使われるもので、設定が推奨されています。

Mailgunが件数に制限はありますが、無料で使えるのでこれを選択しました。

https://www.mailgun.com/

途中の過程はこちらの記事が参考になりました。

https://tech.arms-soft.co.jp/entry/2019/12/11/090000

DNSレコードの設定に関してはCDNにて行いました。
また、SMTPの設定が出ていると思うので、これをMisskeyのコントロールパネルからメールサーバーを選択し、入力すれば完了です。

## アップデート
インスタンス作成自体の作業はこれで終わりなのですが、Misskeyのアップデートをする際の記述もしておきます。
```bash:bash
$ git stash
$ git checkout master
$ git pull
$ git submodule update --init
$ sudo docker pull misskey/misskey
$ sudo docker compose stop && sudo docker compose up -d
```
これだけで終わりです。

# おわりに
ひとまずこれで、小規模なインスタンスは完成です！お疲れ様でした！
自分でインスタンスを立てるとやりたい放題できますし、身内のコミュニティも作れますし、何より作っていく過程(主にトラブルシューティング)が楽しかったので満足です。

読んでいただき、ありがとうございました。


# 参考文献

https://7ka.org/install-misskey-on-docker/