Dockerでお手軽Pleromaインスタンス構築

# はじめに

Mastodonの極小規模な身内向けインスタンスを運用してそろそろ1年近くが経った昨今、Pleromaメイン開発者の[lainさん](https://pleroma.soykaf.com/users/lain)によるPleroma解説「 [青少年のためのぷれろま入門。](https://blog.soykaf.com/post/what-is-pleroma/) 」(英語) を読んだ。Mastodonとも接続可能なElixirで書かれた超軽量な分散SNSであることは知っていたが、改めて興味が湧いてきた。[^1] というわけで、興味本位でMastodon同様にConoHa VPS上にdockernizeしてデプロイして運用を開始したので、その作業内容を記載しておく。

[^1]: Mastodonは良く出来てると思うんだけど、やっぱ結構リソース食うし、しかもリソース消費がどんどん増えてくのでユーザ数増やす気にならなかった。

主として、以下の記事通りの設定をdocker上で実現することを目指し、かつ今まで検討したdockerizeの構成を反映してtweakしている。

> Pleromaメイン開発者の[lainさん](https://pleroma.soykaf.com/users/lain)によるPleromaインストール方法:
> [The magical world of Pleroma, setting up your instance.](https://i2p.rocks/blog/the-magical-world-of-pleroma-setting-up-your-instance.html)


# 前提条件

前提条件として、docker-ceおよびdocker-composeが導入済みのUnix互換環境 (macOS X HighSierraとかUbuntu16.04LTSとか) を仮定する。また、dockerによる`jwilder/nginx-proxy`と`jrcs/letsencrypt-nginx-proxy-companion`が動いているものとする。`nginx-proxy`と`letsencrypt-nginx-proxy-companion`の設定については、以下の関連記事を参考のこと。

> [ConoHa VPSへMastodonインスタンスを引っ越しメモ](https://qiita.com/jqtype/items/09eaa2e76d1ecba3543a)

想定する構成は、上記のMastodonインスタンスと同様に以下の構成。

```
nginx-proxy <--(front bridge)--> nginx <--(Pleroma用back bridge)--> Pleroma & PostgreSQL
```

# 設定ファイルの作成

## Pleromaのソースコード取得

`mypleroma`ディレクトリでの作業という前提でこの先話を進めていこう。まずはソースコードを[PleromaのGitLab](https://git.pleroma.social/pleroma/pleroma/)から取得しよう。

```shell
$ mkdir -p mypleroma
$ git clone https://git.pleroma.social/pleroma/pleroma.git mypleroma
$ cd mypleroma
```


## `Dockerfile`と`docker-compose.yml`の記載

Pleromaをビルドするための`Dockerfile`と、nginxおよびPostgreSQLを含めたシステム全体を規定する`docker-compose.yml`をソースと同じディレクトリ(`mypleroma`)に置く。筆者はそれぞれ以下のように書いている。初期設定後、デプロイ前に書き換えたほうが良いと思われる部分など、それぞれに注意点がある。

### `mypleroma/Dockerfile`の記載

```dockerfile:mypleroma/Dockerfile
FROM elixir:alpine

LABEL maintainer "my name and email address"

WORKDIR /pleroma

RUN apk -U upgrade \
    && apk add -t build-dependencies \
       build-base \
    && apk add git \
    && rm -rf /tmp/* /var/cache/apk/* \
    && mkdir /pleroma/uploads

COPY . /pleroma

ENV MIX_ENV dev
RUN cd /pleroma \
    && mix local.hex --force \
    && mix local.rebar --force \
    && mix deps.get

VOLUME /pleroma/uploads/ # インスタンスにアップロードされたメディアファイルとかの永続化
VOLUME /pleroma/config/ # configファイルの永続化
VOLUME /pleroma/priv/  # ちょいちょいデザインいじるるため該当部分を永続化
```

[lainさんによる解説](https://i2p.rocks/blog/the-magical-world-of-pleroma-setting-up-your-instance.html)の通りに勧めるならば、上記ファイル中の`ENV MIX_ENV dev`については、本来は`ENV MIX_ENV Prod` (Production環境としての設定) とすべきだ。しかし、コンテナビルド後に生成する設定ファイル`config/prod.secret.exs`がビルド前に存在しない場合、環境変数`MIX_ENV=prod`でのコンテナビルド(正確には`mix deps.get`)が通らない。そのため、ここでは敢えてDevelopment環境でのビルド設定としてる。初回だけDevelopment環境となるが、後のアップデート時などにProduction環境へ移行させることが可能だ。

### `mypleroma/docker-compose.yml`の記載

`<your pleroma fqdn>`および`<your email address>`は適宜書き換えてほしい。

```docker:mypleroma/docker-compose.yml
version: '2'
services:
  nginx:
    image: nginx:alpine
    container_name: pleroma-nginx
    expose:
      - "20091"
    restart: always
    tty: false
    environment:
      - "VIRTUAL_HOST=<your pleroma fqdn>"
      - "VIRTUAL_PORT=20091"
      - "VIRTUAL_PROTO=https"
      - "LETSENCRYPT_HOST=<your pleroma fqdn>"
      - "LETSENCRYPT_EMAIL=<your email address>"
      - "LETSENCRYPT_TEST=false"
    links:
      - web
    volumes:
      - ./setting/nginx/conf.d:/etc/nginx/conf.d
    volumes_from:
      - container:proxy-nginx
    networks:
      - front
      - back-pleroma

  db:
    restart: always
    image: postgres:9.6-alpine
    container_name: pleroma-db
    environment:
      POSTGRES_USER: pleroma
      POSTGRES_PASSWORD: pleroma
      POSTGRES_DB: pleroma_dev
    volumes:
      - ./postgres:/var/lib/postgresql/data
    networks:
      - back-pleroma

  web:
    build: .
    image: pleroma
    container_name: pleroma-web
    environment:
      MIX_ENV: dev
    restart: always
    stdin_open: true
    tty: true
    command: mix phx.server
    expose:
      - "4000"
    links:
      - db
    depends_on:
      - db
    volumes:
      - ./uploads:/pleroma/uploads # インスタンスにアップロードされたメディアファイルとかの永続化
      - ./config:/pleroma/config # configファイルの永続化
      - ./priv:/pleroma/priv # ちょいちょいデザインいじるるため該当部分を永続化
    networks:
      - front
      - back-pleroma

networks:
  front:
    external: true
  back-pleroma:
    external: true
```

今更ダサいversion 2なのは容赦願いたい。`docker-compose.yml`については注意点が2つある。

1点目は`Dockerfile`における`ENV MIX_ENV dev`と同様に、`MIX_ENV: dev`だ。こちらも設定ファイル生成時に

2点目は、`POSTGRES_PASSWORD: pleroma`だ。このままでも構わないのだが、設定ファイルを生成した際に作られるランダムパスワードに、後ほど差し替える。

## nginx設定ファイルの記載

[この記事](https://qiita.com/jqtype/items/09eaa2e76d1ecba3543a)同様にnginxの設定ファイルを記載する。今回は、PleromaにおけるMediaProxy[^2]をオンにするため、その設定を入れている。また、[この記事](https://qiita.com/jqtype/items/95b40c47e2bb74805475)に準じていつでも国ごとアクセスをBANできるようにしている。`<your pleroma fqdn>`は適宜書き換えてほしい。

[^2]: 一旦ホストにリモートサーバの画像とかをキャッシュしておく機能。ユーザのIPアドレスを、リモートサーバに対して秘匿することができる。

```nginx:mypleroma/setting/nginx/conf.d/default.conf
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 20091 ssl;
  server_name <your pleroma fqdn>;

  ssl_protocols TLSv1.2;
  ssl_ciphers EECDH+AESGCM:EECDH+AES;
  ssl_ecdh_curve prime256v1;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  ssl_certificate     /etc/nginx/certs/<your pleroma fqdn>/fullchain.pem;
  ssl_certificate_key /etc/nginx/certs/<your pleroma fqdn>/key.pem;
  ssl_dhparam         /etc/nginx/certs/dhparam.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  set_real_ip_from 172.18.0.0/16;
  real_ip_header X-Forwarded-For;

  location / {
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://web:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /proxy {
    proxy_cache pleroma_media_cache;
    proxy_cache_lock on;
    proxy_pass http://web:4000;
  }

  error_page 500 501 502 503 504 /500.html;
}

proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
```


# Pleromaのビルド・デプロイ

dockerおよびnginxの設定ファイルが書き上がったのでビルド作業を開始する。まずは仮想ブリッジを生成する。

## docker仮想ブリッジの生成

- front: nginx proxyと他のdocker-composeで動くサービスを繋ぐ（既に存在する前提)
- back-pleroma: pleromaの各コンテナ間を繋ぐ

```shell
$ docker network create --driver bridge back-pleroma
```

## コンテナビルドとPleroma設定ファイルの生成

まずはpleromaのコンテナをビルドする。

```shell
$ docker-compose build
```

続いて、以下のコマンドを実行して、設定用ファイル `mypleroma/config/generated_config.exs` を生成、ファイル名を`prod.secret.exs`とする。

```shell
$ docker-compose run --rm web mix generate_config
$ cp config/generated_config.exs config/prod.secret.exs
```

ドメイン名や連絡用メールアドレスなど、いくつか入力を促される。適宜設定しよう。

```shell
What is your domain name? (e.g. pleroma.soykaf.com): <your pleroma fqdn>
What is the name of your instance? (e.g. Pleroma/Soykaf): <your instance name>
What's your admin email address: <your email address>
Do you want to activate the mediaproxy? (y/N): y
What is the mediaproxy's URL? (e.g. https://cache.example.com):  # 空白

Writing config to config/generated_config.exs.
```

ここで生成した`prod.secret.exs`を以下のように少々書き換える。また、`<generated postgres password>`をコピーしておこう。

```diff:mypleroma/config/prod.secret.exs
use Mix.Config

config :pleroma, Pleroma.Web.Endpoint,
   url: [host: "<your pleroma fqdn>", scheme: "https", port: 443],
   secret_key_base: "<generated secret keybase>"

config :pleroma, :instance,
  name: "<your pleroma fqdn>",
  email: "<your email address>",
  limit: 5000,
  registrations_open: true

config :pleroma, :media_proxy,
  enabled: true,
+  redirect_on_failure: true
-  redirect_on_failure: true,
-  base_url: ""

# Configure your database
config :pleroma, Pleroma.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "pleroma",
  password: "<generated postgres password>",
  database: "pleroma_dev",
  hostname: "localhost",
  pool_size: 10

```

さて、前節で述べたように上記作業までは暫定的に`MIX_ENV=dev`としていた。このまま今後も`dev`にしておいても良いが、デバッグメッセージが不要に出てくるのも運用上ちょっと邪魔。そのため、前節で設定ファイルを生成したため、此処から先は`MIX_ENV=dev`と`docker-compose.yml`内の記述を変更しておこう。さらにPostgreSQLのパスワードを設定しておく。

```diff:mypleroma/Dockerfile
+ ENV MIX_ENV prod
- ENV MIX_ENV dev
```

```diff:mypleroma/docker-compose.yml
+ MIX_ENV: prod
- MIX_ENV: dev
+ POSTGRES_PASSWORD: <generated postgres password>
- POSTGRES_PASSWORD: pleroma
```

## PostgreSQL DBの設定とmigrate

起動前最後の手順だ。PostgreSQLをmigrateしておく。

```shell
$ docker-compose run --rm web mix ecto.migrate
```

## 起動

通常通り、以下の手順で全コンテナを起動する。

```shell
$ docker-compose up -d
```

起動時にコンパイルが走るので起動自体はちょっと時間がかかる。`docker-compose logs`を使うなりして起動を確認した後に、`https://<your pleroma fqdn>/`にアクセスして起動を確認してみよう。[^3]

[^3]: なお、`https://<your pleroma fqdn>/web`にアクセスするとPleromaの通常フロントエンドではなく、Mastodonのフロントエンドで動くPleromaが現れる。

# 運用開始後

## 定期的なアップデート
リリース毎にtagが打たれていたりするわけではなく、masterに次々commitが上がっていくタイプのプロジェクト運用が為されているようだ。このため、基本的にはmasterブランチを追従するしかない。上記デプロイ手順をそのままなぞり、以下の手順でアップデート可能だ。`prod.secret.exs`は生成済みなので`MIX_ENV=prod`に固定しっぱなしで問題ない。

```shell
$ git pull
$ docker-compose build
$ docker-compose run --rm web mix ecto.migrate
$ docker-compose up -d
```

## 負荷チェックとか色々
いわゆるWebサービス運用なので、サーバ負荷とかセキュリティ問題なんかの不安はある。適宜チェックしておこう。

# まとめ
Mastodon比でとんでもなく軽くて驚嘆した。

Mastodon/Pleroma界隈、治安も良くて変なエンジニアも多くて面白いので、英語が苦にならなければ触って遊ぶと勉強にもなるんじゃないかと思う。個人的にも少しずつ寄与していきたいと思っている。