カフェインとMastodon2.5.1リレー機能

カフェイン

授業とミーティングを終えてコーヒーを飲んだら、飲んでいる最中は何もなかったのに立ち上がってから酷いめまいに襲われた。低血圧なので立ちくらみはよくあるが、立ちくらみのときは視界が白くなる。今回は純粋に平衡感覚だけが狂うというはじめての経験だった。友人に肩を借りてなんとか研究室まで戻り、やらなきゃならない仕事を片付けてからすぐに帰宅した。おそらく季節による体調変動とカフェインの刺激が重なったのだろう。

カフェインと言えば、緑茶にも多く含まれる。私の出身地は茶の産地であり、小学校の総合学習では茶について学ぶ。学んだことを発表するまで含めて総合学習なのだが、茶の効能を歌にするグループがあったような気がする。「カッテキン\カフェイン/カッテキン\カフェイン/」というリズムを今でも覚えている。

僕は24歳だ。(なぜか)まだ学生だし世間的には若者だろうが、一方で身体機能は既にピークを超えたものもある。たとえば大食いは20歳ごろがピークだった。という話は以前もしたような気がする。

Mastodonを立て直した

自分の変更点を残しつつ最新版に追従しようとしたらgitの闇に飲まれてわけがわからなくなったのでリセットボタンポチーした。何回ぶっ壊してもいいVPSとかいうおもちゃ最高。そうまでしてv2.5.1にバージョンアップした理由はリレー機能だ。

マストドン、「連合リレー」対応の2.5.0公開

Activity Pubを受け取って登録済みのインスタンスにそのまま流すリレーサーバーが開発されており、Mastodon側からも対応したとのことだ。管理画面でリレーのURLを入力するだけで接続できる。これを使うと何もしなくても連合TLに同じリレーに属するインスタンスの投稿が流れてきて、賑やかになる。

自分のインスタンスを立てて痛感したのは初速の重要さだ。小規模インスタンスの場合登録しても結局他のインスタンスの知り合いや有名人をフォローしに行くことになり、Mastodonの特徴であるごった煮高速TLを楽しむことができない。リレー機能はここに一石を投じるものだと思う。

今日のUI心理学

5.6は視覚探索の話。視野のたくさんの図形から1つの目標図形を探すとする。このとき目標図形をターゲット、それ以外をディストラクタと呼ぶ。素朴に考えれば私達は図形を一つ一つ見て確認していくはずだ。ターゲットを何番目に見るかの期待値はディストラクタの数の半分なので、見つけるまでにかかる時間はディストラクタ数が増えるに従って線形に増加するはずだ。以下のような画像から「右上を向いた赤」を探すときは実際にそうなり、右のほうが長くかかる。

しかしこれには例外がある。以下の画像から「右上を向いたもの」を探すときや

以下の画像から「緑」を探すとき。

このようなときはディストラクタの数に関係なく一瞬で見つかる。この現象を「ポップアウト」と呼ぶ。脳内の情報処理で「色」や「方位」それぞれのマップを作って探すときは全体を一瞬で処理できるが、複数種類の特徴を統合して判断する処理は一箇所ずつしかできないと考えられている。詳しく知りたい人は「特徴統合理論」でググって。

参考文献:『視覚科学』横澤一彦

初速

自分で立てたマストドンインスタンスに人がこない。マストドンの本来の意義は情報の発信や保管を人に任せないという点で、それは自分のインスタンスに人がいなくても問題はない。

しかしインスタンスをわざわざ立てたきっかけはツイッターのユーザーストリーミングの廃止だ。アニメを見ながら大量かつ高速にツイートするアニメ実況がやりにくくなった。マストドンなら今までどおりの使用感で実況ができると思ったんだが、自分一人しかいないインスタンスでは実況しても何も楽しくない。実況の醍醐味は一体感だからだ。

マストドンユーザーで他にアニメを実況している人間がいないか探してみたが、そもそも 探す方法がない。プロフィール検索は存在しないし、地道に大手インスタンスのトップページで公開されているタイムラインからアニメ実況している人を探したが、pawooに一人いただけだった。

ツイッターからマストドンに人を引き抜くためには、初速が大事だ。クラスタを丸ごと同時に引き抜かないと定着しない。そのための方法を何か考えるべきだった。

というわけでアニメ実況したい人のためのインスタンスanimedon.chao.tokyoをよろしく。

Mastodonに自動ハッシュタグ機能をつけた

アニメ実況時は同じハッシュタグで継続的にツイートする。よってハッシュタグを保持し自動的に付加し続けてくれる機能は便利だ。作った。

やたらとトゥートの反映が遅いのはサーバーがイマイチだからかな…

この機能追加はReact+Reduxの処理階層を知っていれば全然難しくない。まずハッシュタグ入力欄と、そこへの入力を処理するaction, reducer, stateを作る(全部compose-formのコピー)。さらにCtrl+Enterによるトゥートを処理するhandleKeyDownにShift+Enterによって発火する別ルートのトゥート投稿処理を追加する。これもトゥートボタンによって発火する処理をほとんどコピー。そのルートの最後にあるのがこれ。

const status = getState().getIn(['compose', 'text'], '') + ' ' + getState().getIn(['compose', 'preservedHashtag']);

これだけが実質的に意味のあるコードだ。興味のある方はコミットログをどうぞ。

1年前のドワンゴインターンではJavaScriptが全然わからず(わからなかったのはJavaScriptだけではないが)チームメンバーに教えてもらっていたが、今では自力で触れるようになった。

Mastodonでハッシュタグ補完すると1文字目が勝手に確定する問題

Mastodon(v2.5.0rc1)でトゥート入力時にペーストでハッシュタグを入力すると、ハッシュタグ補完機能でハッシュタグの候補が表示される。それをEnterやクリックで確定して投稿すると、次のトゥートを入力するときに日本語入力を有効にしていても1文字目が勝手に確定される。

1文字目が勝手に確定されるという現象は割とよくあるやつで、slackでもTwitter公式でも発生した事例はある。しかし今回はハッシュタグ補完時のみという条件があるので、もしかしたらハッシュタグ補完が発生したときにはトゥート投稿時の投稿欄の情報リセット忘れがあるのかもしれない。現在ソースコードを確認中だ。

なおfriends.nicoでも同様の現象が起きることを確認済み。OSはUbuntu18.04で日本語入力はmozcだ。書いてて思ったが変換の方で何か悪さをしている可能性は十分ある。あるいはデスクトップ環境かも知れないし、ブラウザかも知れない。なかなか手強そうだ。

スプラップアンドビルド

※この記事は『極搾り ピーチ』を飲んで書かれた。

マストドンにelasticsearchをつけようと思ったらなんか壊れてしまったので作り直した。

なんか壊れてしまったというのは、マウント関連だ。リバースプロキシであるnginx-proxyは設定ファイルを外部から読み込むために、ホストの設定ファイル(./my_custom_proxy_settings.conf)をマウントしている。この状態で更にvolumes_fromでmastodon-nginxからnginx-proxyをマウントするとなんだかよくわからないがマウント関連っぽいエラーが出る。read onlyとかなんとか書いてあった。

この2重マウントは僕もわけがわからずやっているが、そんなことだからエラーが出るのは当然だ。面倒なので一回全部消してマストドンを立て直した。前回のデプロイから1週間ちょっとしか経っていないが、github上のデプロイガイドが書き換わっていて驚いた。mastodonの進化はまことに速い。

新しいdocker-compose.ymlはこうだ。今回は自分で改造することも視野に入れ、自前でビルドすることにした。なおmastodonのバージョンはv2.5.0rc1だ。

version: '3'
services:

  mstdn-nginx:
    image: nginx:1.11.10-alpine
    expose:
      - "9090"
    restart: always
    tty: false
    env_file: .env.production
    links:
      - web
      - streaming
    volumes:
      - ./setting/nginx/conf.d:/etc/nginx/conf.d:ro
      - ./setting/nginx/conf:/etc/nginx/conf/:ro
      - /var/www/certs:/etc/nginx/certs:ro
    networks:
      - common_link
      - back-mastodon
  db:
    restart: always
    image: postgres:9.6-alpine
    networks:
      - back-mastodon
### Uncomment to enable DB persistance
    volumes:
      - ./postgres:/var/lib/postgresql/data

  redis:
    restart: always
    image: redis:4.0-alpine
    networks:
      - back-mastodon
### Uncomment to enable REDIS persistance
    volumes:
      - ./redis:/data

  es:
    restart: always
    image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.1.3
    environment:
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    networks:
      - back-mastodon
### Uncomment to enable ES persistance
    volumes:
      - ./elasticsearch:/usr/share/elasticsearch/data

  web:
    build: .
    image: tootsuite/mastodon
    restart: always
    env_file: .env.production
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000 -b '0.0.0.0'"
    networks:
      - back-mastodon
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      - db
      - redis
#      - es
    volumes:
      - ./public/assets:/mastodon/public/assets
      - ./public/packs:/mastodon/public/packs
      - ./public/system:/mastodon/public/system

  streaming:
    build: .
    image: tootsuite/mastodon
    restart: always
    env_file: .env.production
    command: yarn start
    networks:
      - back-mastodon
    ports:
      - "127.0.0.1:4000:4000"
    depends_on:
      - db
      - redis

  sidekiq:
    build: .
    image: tootsuite/mastodon
    restart: always
    env_file: .env.production
    command: bundle exec sidekiq -q default -q mailers -q pull -q push
    depends_on:
      - db
      - redis
    networks:
      - back-mastodon
    volumes:
      - ./public/packs:/mastodon/public/packs
      - ./public/system:/mastodon/public/system
## Uncomment to enable federation with tor instances along with adding the following ENV variables
## http_proxy=http://privoxy:8118
## ALLOW_ACCESS_TO_HIDDEN_SERVICE=true
#  tor:
#    build: https://github.com/usbsnowcrash/docker-tor.git
#    networks:
#      - common_link
#      - back-mastodon
#
#  privoxy:
#    build: https://github.com/usbsnowcrash/docker-privoxy.git
#    command: /opt/sbin/privoxy --no-daemon --user privoxy.privoxy /opt/config
#    volumes:
#      - ./priv-config:/opt/config
#    networks:
#      - common_link
#      - back-mastodon

networks:
  common_link:
    external: true
  back-mastodon:
    external: true

注目してほしいのはmstdn-nginxのvolumesでホストマシンの/var/www/certsをマウントしていることだ。nginx-proxyでもホストマシンの同じディレクトリをマウントしている。2重マウントを解消し同じディレクトリをマウントするようにした。

elasticsearchはデフォルトで日本語対応してません。日本語対応プラグインを入れるのはちょっとしんどそうなのでまた今度やります。

ぶっ壊してまたイチから作ってというのを繰り返せるのは楽しい。無限にやりたい。

Mastodonを立てる(Docker + nginx-proxy + letsencrypt-nginx-proxy-companion)2018年8月版

1.nginx-proxy + letsencrypt-nginx-proxy-companion

すでに同じサーバー上で他のアプリを動かしていたので、nginx-proxyを使った。ついでにletsencrypt-nginx-proxy-companionも使い、https化も自動化した。まずこの2つでdocker-compose.ymlを書いておく。

nginx-proxyはデフォルトであるサイズ(1MB?)以上のファイルのアップロードを受け付けない。これを超えるサイズの動画の投稿などを可能にしたいので自前のカスタム設定用のファイルを作り、マウントしている。

dockerネットワークを作る

dockerのコンテナ間の通信はdocker networkで行う。

  • common_linkはmastodonを動かすnginxサーバーとnginx-proxyの通信を行う
  • back-mastodonはmastodonを構成する各コンテナ間での通信を行う
$ docker network create --driver bridge common_link
$ docker network create --driver bridge back-mastodon

なおdocker networkの管理に有用なコマンドを書いておく

# ネットワークの一覧を表示する
$ docker network ls
# ネットワークの詳細を表示する
$ docker network inspect <network名>
# 使用されていないネットワークを削除する
$ docker network prune

動作確認(必要なら)

mastodon以外にすでにdockerで動かしているアプリがある場合、ネットワークの設定を修正する。私はwordpressを動かしていたのでその例を示す。変更点は2つ。

  1. nginx-proxyと通信するコンテナには環境変数としてVIRTUAL_HOST, LETSENCRYPT_HOST, LETSENCRYPT_EMAILを与える。
    • VIRTUAL_HOSTにはサブドメイン名(ここでは違うが)を設定する。のちのちmastodonにはanimedon.chao.tokyoというサブドメイン名を設定するので、chao.tokyoとanimedon.chao.tokyoのどちらにアクセスされたかによって違うコンテナを呼び出せるようになる。なおサブドメイン名はお名前ドットコムで設定した。
    • LETSENCRYPT_HOSTとLETSENCRYPT_EMAILはletsencrypt-nginx-proxy-companionのための環境変数。前者にはVIRTUAL_HOSTと同じ値を、後者にはletsencryptの通知メールを受け取るメールアドレスを設定する。
  2. 先ほど設定したネットワークを使用する
    • トップレベルにnetworksを作り、先ほど作ったcommon_linkというネットワークを利用することを明示する
    • 各コンテナでcommon_linkを利用することを明示する
version: "2"
services:
  wordpress:
    image: wordpress:latest
    container_name: "wp-main"
    volumes:
      - .:/var/www/html
      - ./log:/tmp/log
    depends_on:
      - db
    environment:
      WORDPRESS_DB_HOST: "db:3306"
      VIRTUAL_HOST: chao.tokyo
      LETSENCRYPT_HOST: chao.tokyo
      LETSENCRYPT_EMAIL: 見せられないよ
    env_file: .env
    networks:
      - common_link

  db:
    image: mysql:5.7
    container_name: "wp-db"
    volumes:
      - "./db-data:/var/lib/mysql"
    env_file: .env
    networks:
      - common_link
networks:
  common_link:
    external: true

nginx-proxyのdocker-composeとアプリのdocker-composeを任意の順序で起動すると、アプリがアクセス可能になるはずだ。この段階でアプリ(例:chao.tokyo)にアクセスできない場合はどこかでエラーが出ているので頑張ってなんとかする。letsencrypt-nginx-proxy-companionは起動に時間がかかるし、初回起動時は特に長い。証明書の取得、あるいはその必要がないことの確認が済むと3600秒のスリープに入るというようなメッセージが出るので、それまではエラーが出ても焦らないで。

2.Mastodonの設定

ではMastodonをデプロイしていく。まず適当な作業ディレクトリにcloneする。

git clone https://github.com/tootsuite/mastodon.git

docker-compose.ymlの編集

公式のインストラクションによると

If you’re not making any local code changes or customizations on your instance, you can use a prebuilt Docker image to avoid the time and resource consumption of a build

もしローカルでコードを変更しないのであれば、ビルド済みのDockerイメージを利用することで時間と資源を節約することができます。

とのことなので、そうすることにした。この場合docker-compose.ymlに若干の変更が必要になる。他にもいろいろな変更が必要になるがとりあえず載せる。

version: '2'
services:
  nginx:
    image: nginx:1.11.10-alpine
    container_name: mstdn-nginx
    expose:
      - "9090"
    restart: always
    tty: false
    env_file: .env.production
    links:
      - web
      - streaming
    volumes:
      - ./setting/nginx/conf.d:/etc/nginx/conf.d:ro
      - ./setting/nginx/conf:/etc/nginx/conf/:ro
    volumes_from:
      - container:nginx-proxy_nginx-proxy_1
    networks:
      - common_link
      - back-mastodon
  db:
    restart: always
    image: postgres:9.6-alpine
    networks:
      - back-mastodon
### Uncomment to enable DB persistance
    volumes:
      - ./postgres:/var/lib/postgresql/data

  redis:
    restart: always
    image: redis:4.0-alpine
    networks:
      - back-mastodon
### Uncomment to enable REDIS persistance
    volumes:
      - ./redis:/data

#  es:
#    restart: always
#    image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.1.3
#    environment:
#      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
#    networks:
#      - back-mastodon
#### Uncomment to enable ES persistance
##    volumes:
##      - ./elasticsearch:/usr/share/elasticsearch/data

  web:
#    build: .
    image: tootsuite/mastodon:v2.4.3
    restart: always
    env_file: .env.production
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000 -b '0.0.0.0'"
    networks:
      - back-mastodon
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      - db
      - redis
#      - es
    volumes:
      - ./public/system:/mastodon/public/system
      - ./public/assets:/mastodon/public/assets
      - ./public/packs:/mastodon/public/packs

  streaming:
#    build: .
    image: tootsuite/mastodon:v2.4.3
    restart: always
    env_file: .env.production
    command: yarn start
    networks:
      - back-mastodon
    ports:
      - "127.0.0.1:4000:4000"
    depends_on:
      - db
      - redis

  sidekiq:
#    build: .
    image: tootsuite/mastodon:v2.4.3
    restart: always
    env_file: .env.production
    command: bundle exec sidekiq -q default -q mailers -q pull -q push
    depends_on:
      - db
      - redis
    networks:
      - back-mastodon
    volumes:
      - ./public/system:/mastodon/public/system
      - ./public/packs:/mastodon/public/packs
## Uncomment to enable federation with tor instances along with adding the following ENV variables
## http_proxy=http://privoxy:8118
## ALLOW_ACCESS_TO_HIDDEN_SERVICE=true
#  tor:
#    build: https://github.com/usbsnowcrash/docker-tor.git
#    networks:
#      - external_network
#      - back-mastodon
#
#  privoxy:
#    build: https://github.com/usbsnowcrash/docker-privoxy.git
#    command: /opt/sbin/privoxy --no-daemon --user privoxy.privoxy /opt/config
#    volumes:
#      - ./priv-config:/opt/config
#    networks:
#      - external_network
#      - back-mastodon

networks:
  common_link:
    external: true
  back-mastodon:
    external: true

変更点を1つずつ説明する。

nginxコンテナの追加

mastodon専用のサーバーのコンテナ。nginx-proxyと各コンテナを中継する。volumesでコンテナの外から設定ファイルを編集できるようにしている。volumes_fromに関する解説はここを参照のこと。networksでcommon_linkと先ほど使用したmastodonの内部用ネットワークの両方に接続する。

なおvolumes_fromはdocker-compose version 3では使えない。解決策を調べたがよくわからなかったのでversionを2にした。

dbコンテナ, redisコンテナ

データベースの永続化のためにvolumesをコメントアウトした。先ほど使用したmastodon内部用ネットワークを利用するためにネットワーク名を変えた。

webコンテナ

ローカルで変更を加えないことにしたのでbuildをコメントアウト。imageでバージョンを指定。サーバー外部との通信はnginxコンテナが一括して担うのでnetworksはback-mastodonのみでよい。

volumesコンテナにアセットが保存されるディレクトリを追加。ここはエラーメッセージを頼りに手探りでやったことなのでよく理解できていないのだが、ビルドをしない場合はこうしないと動かないっぽい(参考)?。

streamingコンテナ

buildをコメントアウト。networksをback-mastodonに。

sidekiqコンテナ

これも手探りでやったのでよくわからないが、volumesに/public/packsを追加した。もしかしたら要らないかも。

networks

先ほど作ったcommon_linkとback-mastodonを利用することを明示する。

mastodon用nginxの設定ファイルの編集

mastodon/settings/nginx/conf.d/default.confを編集する。参考をコピペしてドメイン名等を埋めていけばいいが、add_header Content-Security-Policyの行はそのままだと画像アップロードができないので、意味がわかる人なら画像アップロードを許可するようにここを編集する。僕はわからなかったので丸ごと消した。

.env.productionの編集

まずcp .env.production.sample .env.productionし、このファイルを編集していく。

# 冒頭に追加
VIRTUAL_HOST=animedon.chao.tokyo
VIRTUAL_PORT=9090
VIRTUAL_PROTO=https
LETSENCRYPT_HOST=animedon.chao.tokyo
LETSENCRYPT_EMAIL=見せられないよ
LETSENCRYPT_TEST=false

# 探して編集
LOCAL_DOMAIN=animedon.chao.tokyo

# それぞれdocker-compose run --rm web rake secretを実行し出力された文字列を入れる
SECRET_KEY_BASE=
OTP_SECRET=

# 探して編集
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_LOGIN=見せられないよ
SMTP_PASSWORD=見せられないよ
SMTP_FROM_ADDRESS=見せられないよ
SMTP_DOMAIN=gmail.com
SMTP_OPENSSL_VERIFY_MODE=none

冒頭はnginx-proxyとmastodon用nginxが通信するための環境変数。

メールはgmailを利用するのでこうなった。なおgmail側で事前に設定してmastodon用のアプリパスワードを発行するのが望ましい。

起動

docker-compose build
chown -R 991:991 public
docker-compose run --rm web bundle exec rake mastodon:setup

chownは以外なタイミングでトラブった記憶があるので(忘れた)、それっぽいエラーが出たら思い出したかのようにもう一度実行しよう。

最後のコマンドで対話型のセットアップが始まる。内容はここにある。

  1. ドメイン名→animedon.chao.tokyo
  2. シングルユーザー?→No
  3. Docker?→Yes
  4. DB設定→Enter連打
  5. Redis設定→Enter連打
  6. クラウドストレージ→No
  7. メール設定→NoとEnter連打。さっき設定したのでたぶん上書きはされてない。
  8. DBマイグレーション→永続化してあるので既にやってあるならスキップしないとエラーになる
  9. アセットのプリコンパイル→同上。ただしこれはやり直してもよい。とても時間がかかる。
  10. 管理者アカウント作成→しましょう
docker-compose up -d

起動します。

終わりに

参考にしたサイトはどれも素晴らしかったが、mastodonの進化が非常に早いのでそのままでは動かないところがいくつもあった。基本的な構築の流れは参考サイトを見てもらったほうがよくて、私のこの記事はどこにどういうエラーが起こりうるのか、それをどう考えて対処したのかというような記録として残しておきたいと思う。

参考

学会2日目

ポスターセッションでは面白い研究が多く、やっている人たちも楽しんでいるのが伝わってきた。研究は楽しい!研究は楽しい!楽しい!楽しい!

Mastodonは立った→animedon.chao.tokyo

僕もあなたも逮捕されない範囲内で好きに使ってください。

立てたけどそんなに使わなそう。2018年8月20日現在のMastodonの立て方にはとても詳しくなったので、この情報の価値が高いうちにまとめておきたい。立ったあともCSPとか画像・動画のサイズ上限とかメールとか適切に設定しないと上手く動かないのでなかなか難しいぞ。立てる順番が違うと壊れるというDockerにあるまじき事件まで起きたからな。

次はフロントエンドの改造に着手したい。自動ハッシュタグ挿入機能と公開範囲設定保持機能。

Mastodon立ったり立たなかったり

nginx_proxy + letsencrypt-nginx-proxy-companion + dockerの構成。

どこの情報を見てもそのまま動くものはなかったので苦労している。かき集めてきた情報でとりあえず動くところまで言ったので備忘録を兼ねて設定を晒す。

全体的に情報が古い。検索に引っかかる情報は2017年前半のMastodon黎明期のものが多いが、OSSであるMastodonの進化のスピードは非常に速く数カ月前の情報でも役に立たないことがある。

比較的新しくて参考になった記事:Ubuntu16.04にDockerでMastodonインスタンスを立てる


残る問題点

Mastodonを立てるぞ1

ツイッターもUserStreamの廃止でとうとう終わりだろうと思ったので。

まずサーバー上でこのホームページ(wordpress)が稼働しているのでどうしたものかと調べ、nginx_proxyを採用することにした。こいつはアクセスされたサブドメインに応じて違うコンテナにリクエストを振り分けてくれる。なかなか難航したが導入した。ついでにSSL証明書の取得まで自動化された。

次はMastodonのデプロイだ。docker-compose.ymlを編集してサーバーまで同時に起動するようにし、さらにそれがnginx_proxyの管理下になるようにnetworksの設定をイジイジする。絶対うまくいってない気がするが現在アセットのコンパイル中なので結果はまだわからない。

俺を起こさないでくれ。死ぬほど疲れてる。