1
0
hugo/old/2017-04-01-mastodon.md
2024-12-21 22:23:54 +09:00

1381 lines
59 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

+++
date = "2017-04-01"
tags = ["heroku","sns", "mastodon"]
title = "Heroku+Mastodon"
slug = "heroku-mastodon"
+++
activitypubというprotocolがあります。わかりやすく言うと、分散snsのprotocolです。このprotocolを使ったserverで有名なのはmastodonです。herokuで立ててみたので、その運営に関するまとめです。
[https://mstdn.syui.ai/](https://mstdn.syui.ai/)
### TwitterとMastodon
Twitterには前から不穏な空気はあって、主に経営が云々という理由などからですが、一体どこに移転しようかということを思っていたユーザーは多かったのではないでしょうか。
私は、Twitterは結構好きで、アイコンとかアプリとかすごいよくできていて面白いし、ユーザーも多いし、シンプルだし色々素晴らしいのですが、しかし、経営があまりうまくいってないみたいな話があって。
で、こういった視点からみた場合、私は、大企業よりも個人の方を若干、信頼している部分もあって、そのあたりでTwitter以外の選択肢というものがあればいいのになと思っていました。
ただ、Twitterに代わるサービスというものはなかなかありませんでした。
これは主にサービス内容というよりユーザー数みたいなものが重要で、特にSNSなので人がいないと意味がありません。自分がアカウント持ってなかったり、または相手が持っていなかったり。
この点、Twitterはユーザー数が圧倒的に多くて、誰もがアカウントを持っています(大抵の場合)。なので、やり取りを行いたい場合の気軽さがあり、これがTwitterの魅力の一つでした。いくら良いサービスを知っていても、そこに人がいなければ、なかなかSNSサービスを使う気にはならないのです。もちろん、サービスとしての利便性と言うのは重要です。ただ、いちばん重要なのはユーザー数とか、自分の周りの人たちが使っているかどうかだと思います。
次に、経営的な観点というか、倫理観みたいな話をしていきたいと思います。
これについては、私は先程、企業よりも個人の方を若干信頼していると言いましたが、基本的には個人の方を信頼します。あくまで比較の問題ですし、ケースバイケースですが、基本姿勢としてはそうです。もちろん、悪い人もいれば、裏切られることもあるでしょう。しかし、それは大企業でも同じだと思います。
何故かと言うと、一つは経営に行き詰まった企業でかつ一時期とても反映してた大企業が衰退する時というのは、歴史的な視点で見た場合、多くの悲惨な事件を引き起こしました。
もちろん、これは個人で見た場合も同じですが、大企業なら安心というわけではないと私は考えます。私は、大企業でも何をするかわからない、そう考えているのです。
そして、個人よりも大企業ほうが内部的な事情は外からは見えにくいのです。したがって、監視という視点から見ても、個人を監視するより大企業を監視することのほうが難しいと私は思います。基本的には。
ただ、mastodonでもそれを悪用しようとする人達は出てくると思われます。その点には注意しなければなりませんが、暫くの間、個人的にはmastodonをやってみようかなと思っています。使い続けるかどうかは今のところ分かりませんが、私は単に自分が使ってみて良いと思うものを使うと思います。
### 使ってみた感想など
巷では設計に失敗しているとか、すぐに飽きられるなど言われていますが、私はそれはまだわからないと考えています。
例えば、設計に失敗しているとか、すぐに飽きられるなどの問題は単なる結果でしかありません。どういうことかというと、結果が良ければ設計に成功していると言われ、結果が悪ければ設計に失敗している言われることになるでしょう。
ただ、私が思うのは、偉そうなことばかり言う人は完璧な設計でなおかつ多くのユーザーに使われるものを自分で作ればいいのではないかと思います。
しかし、そういう人に限って全く設計できなかったり、または言ってることが最終的にデタラメ(ハズレまくり)だったりすることが多いのは、単なる気のせいでしょうか。
確かに、一時的にも注目を集めたものって色々言われるのが世の常なのでしょうけれど、やっぱり定着するのか、すぐに廃れるのかは分かりません。
私が使ってみた感想で言うと、なんか賑やかな感じで結構面白いと思いました。
普通に使いやすいですし、色々な使い方ができそうというのは思いました。
### tootsuite/mastodon
実験的にサーバーを立ち上げてみました。連絡は [@syui@mstdn.syui.ai](https://mstdn.syui.ai/@syui) よりお願いします。mastodonはこちらで使ってこうかなと思ってるけど、やっぱり.cloudのほうが人いっぱいで面白そうだなーというのはあるので、どうなるかはわからないですが。
[https://mstdn.syui.ai/](https://mstdn.syui.ai/)
説明 : [https://mstdn.syui.ai/about/more](https://mstdn.syui.ai/about/more)
mastodonの使い方だけど、基本的にはdockerで動かす形になる。
簡単に言うとこちらのリポジトリ[tootsuite/mastodon](https://github.com/tootsuite/mastodon)を見ながら使用されている技術などを読み解きながらHeroku Deployして色々やる。
まずは確認から。なんとなく使われてる技術、どうやって動いてるのかなどをゆるく把握する。
```bash
# https://github.com/tootsuite/mastodon/blob/master/README.md
$ docker-compose build
$ docker-compose run --rm web rake secret
ここで作成されたキーが3個必要なので、上のコマンドを3回ほど実行
$ cp .env.production.sample .env.production
$ vim !$
ここにキーを書いたり、基本設定を書いていく。具体的に必要な環境変数についてはheroku deployの方の設定ファイルなどが分かりやすいので下記参照。
$ docker-compose run --rm web rails db:migrate
$ docker-compose run --rm web rails assets:precompile
$ docker-compose up
# LOCAL_DOMAINに書いたものにアクセス(最初はlocalhost:3000などで登録して確認する)
$ curl localhost:3000
```
次に、Heroku Deployを実行してみる。簡単に.envに書いた値などをGUIに入れてく。
https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Heroku-guide.md
example :
```bash
LOCAL_DOMAIN=appname.herokuapp.com
LOCAL_HTTPS=true
# Amazonは使わないのでHerokuのworkerをONにすることで画像などを保存できるようになる(が永続化するかは不安定という予想)
S3_ENABLED=false
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_LOGIN=username@gmail.com
SMPT_PASSWORD=2FA_APP_PASSWD
SMTP_FROM_ADDRESS=username@gmail.com
```
通知はGmailを使う。2FAを設定している場合はアプリパスワードを発行する。
立ち上げた後は`https://appname.herokuapp.com/about`にてサインアップ。
次に以下のコマンドなどを実行し、サインアップしたユーザーを管理者に昇格させる。
```bash
$ heroku git:clone -a appname
$ cd appname
$ heroku run rails db:migrate
$ heroku rake mastodon:make_admin USERNAME=yourUsername
$ heroku config:add SINGLE_USER_MODE=true
```
以下にアクセスして色々設定。
https://appname.herokuapp.com/admin/settings
私はサブドメインで使おうかなーと言うことでCloudFlareにてCNAMEを設定する。なお、CryptoでSSLをFullにしないといけない(Heroku用)。HerokuでCusotom Domainを設定する。
heroku : sub.main.com appname.herokuapp.com
cloudflare : CNAME mastodon appname.herokuapp.com
アクセスできることを確認後に`LOCAL_DOMAIN`を変更することで、多分だけど`@user@sub.main.com`を使えるようになる(ちょっと時間をおかなければならない)。
```bash
$ heroku config:add LOCAL_DOMAIN=sub.main.com
```
あとは、こちらに登録することで補完とかでるようになるのかも(フォローしたりやり取りをすると補完は出るようになる)。登録しなくても使える。
https://instances.mastodon.xyz/list
コードを編集したい場合は元のリポジトリなどからフォークとかクローンしたものを`heroku push`すればOKです。私もちょっと手を加えています。主にレイアウト関連ですが。
### Mastodon運用
Mastodonのインスタンスを管理してきて数ヶ月が経ちました。よって、今までハマったところなどをまとめていけたらと思います。大体はリリースートを見ればいいと思いますが、それだけでは実際の運用を乗り切れない場合が多々存在します。
私は[https://mstdn.syui.ai](https://mstdn.syui.ai)にMastodonの個人インスタンスを立てて遊んでいます。リモートアドレスは`WEB_DOMAIN`を設定しているので`@syui@syui.ai`です。
このインスタンスを立てた理由としては、Twitterの有料化などの話があり、そうした場合の手軽な連絡手段や投稿手段を確保するためでした。
Mastodonは`gnu social`を用いてユーザー同士が手軽につながるような仕組みを利用しています。宛先は`@user@example.com`という形で他のインスタンスとやり取りが可能です。自分のインスタンス内だと`@user`で済みます。
前置きはこのくらいにして、Mastodonのインスタンス運営でハマりそうなポイントを書いていきたいと思います。
v2.8.0 : https://github.com/zunda/mastodon/wiki/CreateInstanceOnHeroku
## Mastodon on Heroku
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?button-url=https://github.com/tootsuite/mastodon&template=https://github.com/tootsuite/mastodon)
## Mastodon Install (Docker)
```sh
$ git clone https://github.com/tootsuite/mastodon.git
$ cd mastodon
# db, redis:volumes uncomment
$ vi docker-compose.yml
db:
restart: always
image: postgres:alpine
volumes:
- ./postgres:/var/lib/postgresql/data
redis:
restart: always
image: redis:alpine
volumes:
- ./redis:/data
$ mkdir -p ./{postgress,radis}
$ docker-compose build
# copy 3 key
$ zsh -c "repeat 3 docker-compose run --rm web rake secret"
$ cp .env.production.sample .env.production
$ vi .env.production
LOCAL_DOMAIN=localhost:3000
LOCAL_HTTPS=false
PAPERCLIP_SECRET=xxx1
SECRET_KEY_BASE=xxx2
OTP_SECRET=xxx3
$ docker-compose run --rm web rails db:migrate
$ docker-compose run --rm web rails assets:precompile
$ docker-compose up
# update web
$ docker-compose stop
$ docker-compose run --rm web rails assets:precompile
$ docker-compose start
# copy
$ docker-compose cp web ./file /mastodon/file
# ssh
$ docker-compose exec web /bin/bash
# reset
$ docker-compose down
$ docker-compose build
$ docker-compose run --rm web rails db:migrate
$ docker-compose run --rm web rails assets:precompile
$ docker-compose up -d
# source build
$ git pull ...
$ docker-compose build
$ docker-compose up -d
# open brewser, sing up
$ docker-compose run --rm web rails mastodon:confirm_email USER_EMAIL=$email
$ docker-compose run --rm web rails mastodon:make_admin USERNAME=$username
```
## DBのリストア
私は、Heroku上でインスタンスを立ち上げています。しかし、Heorku(Free)はDBが`10000`レコード(ROWS)しか保存出来ませんので、無料枠で使うには、定期的にDBをリセットする必要が出てきます。
Mastodonを立ち上げ管理者でログインした直後にDBをバックアップして、そのデータを保存しておく必要があります。この際、dbとの整合性を維持するためmastodonのversionを合わせる必要があるかもしれません。
今回は、そのリストア方法になります。
Mastodonは`psql`を利用しています。よって、以下のような感じでdumpしたデータをリストアできます。
```bash
# リセット(要注意)
$ heroku pg:reset -a $appname
# リストア
$ pg_restore --verbose --clean --no-acl --no-owner -h $host -U $username -d $database ./$filename.dump
```
エラーが出るようなら`heroku pg:psql`して修正するか若しくは、mastodonのversionをバックアップ時のものにダウングレードする必要があります。
## 2FAを無効にする
```bash
$ heroku pg:psql
UPDATE users SET otp_required_for_login=false WHERE email='user@example.com';
```
## 500が出て投稿できない、動かない
v 1.4からは以下のコマンドを実行する必要があります。
```bash
$ heroku run rake db:migrate -a $app
$ heroku run rake assets:precompile -a $app
$ heroku ps:restart -a $app
```
それ以外では以下のコマンドで解決する場合があります。
```bash
$ heroku run rake webpacker:compile -a $app
```
## Your slug size exceeds our soft limit (3XX MB) which may affect boot time.
上記のようなエラーがデプロイ時に出た場合はプラグインをインストールしてキャッシュなどを削除します。
```bash
$ heroku plugins:install heroku-repo
$ heroku repo:purge_cache -a $app
```
## db:migrateでエラーが出る場合
以下を実行するとエラーが出る場合があります。エラー内容によって必要な処理が異なります。
```bash
$ heroku run rake db:migrate -a $app
```
## PG::ForeignKeyViolation
v1.4.1からv1.4.2の間に、外部キーが導入されたのでそれに関するエラーです。
```bash
$ heroku run rake db:migrate -a $app
PG::ForeignKeyViolation
$ heroku run rake mastodon:maintenance:prepare_for_foreign_keys -a $app
$ heroku run rake db:migrate -a $app
$ heroku ps:restart -a $app
```
## PG::DuplicateTable: ERROR
エラー内容には`PG::DuplicateTable: ERROR`のみならず`Index name 'index_accounts_on_uri' on table 'accounts' already exists`を含みます。
これは`v 1.3 -> v 1.4`にした時にでたエラーです。ただし、ダウングレードを数回行っているため、そこで入ったDBの値が悪さをしていたのかもしれません。
```bash
$ heroku run rake db:migrate -a $app
PG::DuplicateTable: ERROR
account_domain_blocks
conversations
conversation_mutes
$ heroku pg:psql
DROP TABLE account_domain_blocks;
DROP TABLE conversations;
DROP TABLE conversation_mutes;
$ heroku run rake db:migrate -a $app
Index name 'index_accounts_on_uri' on table 'accounts' already exists
$ heroku pg:psql
DROP INDEX index_accounts_on_uri;
$ heroku run rake db:migrate -a $app
$ heroku ps:restart -a $app
```
### Error massage (Sample)
```sh
$ heroku run rake db:migrate -a $app
> ActiveRecord::StatementInvalid: PG::DuplicateTable: ERROR: relation "conversations" already exists
> : CREATE TABLE "conversations" ("id" bigserial NOT NULL PRIMARY KEY, "uri" character varying DEFAULT NULL, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
```
```sh
$ heroku run rake db:migrate:reset -a $app
> ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your 'production' database.
> If you are sure you want to continue, run the same command with the environment variable:
> DISABLE_DATABASE_ENVIRONMENT_CHECK=1
```
```sh
$ heroku run rake db:migrate -a $app
> Index name 'index_accounts_on_uri' on table 'accounts' already exists
```
## 個人用インスタンス
```sh
# https://github.com/ummjackson/mastodon-guide/blob/master/single-user-mode.md
$ heroku config:set SINGLE_USER_MODE=true -a $app
```
## .bulidpacks
v 1.3からは必要なパッケージがいくつか増えています。Herokuサーバーにそれをインストールしてやる必要があります。Herokuでは`buildpacks`に`apt`を追加することでパッケージのインストールが可能になります。インストールするパッケージは`Aptfile`に書きます。
```bash
$ cat .buildpacks
$ heroku buildpacks:add --index 1 https://github.com/heroku/heroku-buildpack-apt
```
## gem install idn-ruby
v 1.5では、`Aptfile`に`libidn11-dev`(centosなどではlibidn-develというパッケージ名)が追加されています。これは`wget`でダウンロードされ、`idn-ruby`が依存しています。しかし、Herokuではこのパッケージが元から入っているためか、Aptfileから`libidn11-dev`の行を削除しないと`gem install idn-ruby`がインストールできずビルドが完了しませんでした。
`v 1.5 rc1 - v 1.5 rc3`
```bash
$ heroku run "dpkg -l"
$ vim Aptfile
libidn11-dev という行を削除
```
現在は`branch:master`にて[fix](https://github.com/tootsuite/mastodon/commit/161f72cce3547fabc945b002599880b137d970db)されているので、`libidn11-dev`の行を削除する必要はありません。(Heroku)
> Aptfile
```
+ libidn11
libidn11-dev
```
## gem install cld3
```bash
$ cat Aptfile
$ echo protobuf-lite-devel >> Aptfile
```
## gem install charlock_holmes
```bash
$ heroku buildpacks:add --index 2 https://github.com/rcaught/heroku-buildpack-cmake
```
> Aptfile
```
libicu52
libicu-dev
```
https://github.com/brianmario/charlock_holmes/wiki/Installing-charlock-holmes-and-libicu-dev-on-heroku
## generate_vapid_key
v 1.5から`generate_vapid_key`を発行できるようになりました。
```bash
$ heroku run rake mastodon:webpush:generate_vapid_key -a $app
$ heroku config:set VAPID_PUBLIC_KEY=XXXXXXX -a $app
$ heroku config:set VAPID_PRIVATE_KEY=XXXXXXX -a $app
```
## Sidekiqのスレッド数を減らす
```bash
$ heroku config:set SIDEKIQ_THREADS=2 -a $app
```
## Push rejected, failed to compile Node.js app.
HerokuにDeployする際、以下のエラーが出る場合は、`package.json`にバージョンを指定することで回避できます。
```sh
$ git push heroku master
> ! Push rejected, failed to compile Node.js app.
```
> package.json
```
{
"engines": {
"yarn": "0.27.5",
"node": "8.1.3"
}
}
```
https://github.com/react-boilerplate/react-boilerplate/issues/1779
## tootがdeleteできなくなった
1.6(6c81f9d)でtootが削除できない状態でしたが、以下の手順で解決しました。
```sh
$ heroku buildpacks:remove https://github.com/rcaught/heroku-buildpack-cmake
$ git merge : master(efec5072)
$ heroku repo:purge_cache -a $app
$ heroku config:set MAX_THREADS=2 -a $app
```
```sh
## tootを削除したはずなのに残ってる
## 1.6(6c81f9d)
$ url=https://mstdn.syui.ai/api/v1/statuses/473
$ curl -X DELETE $url -H "Authorization: Bearer $access_token"
> {}
$ curl -X GET $url -H "Authorization: Bearer $access_token"
{
"id": 473,
"created_at": "2017-09-15",
"in_reply_to_id": null,
"in_reply_to_account_id": null,
"sensitive": false,
"spoiler_text": "",
"visibility": "public",
"language": "ja",
"uri": "http://mstdn.syui.ai/users/syui/statuses/473",
"content": "up : master(472df245)",
"url": "http://mstdn.syui.ai/@syui/473",
"reblogs_count": 0,
"favourites_count": 0,
"favourited": false,
"reblogged": false,
"muted": false,
"pinned": false,
"reblog": null,
"application": {
"name": "test",
"website": null
}
}
```
https://github.com/tootsuite/mastodon/issues/4928
## custom emojiでSSLのエラーが出る時
以下のようなエラーが出る場合があります。なぜなら、`img src=http://`となっているからです。
```js
Mixed Content: The page at xxx was loaded over HTTPS, but requested an insecure video xxxx. This content should also be served over HTTPS.
```
Find and fix mixed content https://developers.google.com/web/fundamentals/security/prevent-mixed-content/fixing-mixed-content
しかし、以下のように設定するとうまく動作しました。知らずにPRを出してしまいました。
```sh
$ heroku config:set CDN_HOST=https://${emojiが置いてある場所} -a $app
```
https://github.com/tootsuite/mastodon/pull/5033
## 1.6でのHeroku(Free)運用メモ
- mastodon v1.6
- DB recode 8000
- toot 1500
- follow 0
- w 2
大体、`1500`tootでDBのレコードを`8000`程度消費しました。無料枠は`10000`なのでこのくらいになるとDBをリセットする必要が出てきます。この消費量はタイムラインに表示されるtootなどにも影響を及ぼします。そのため私は、フォロー0にしています。私の場合、`2ヶ月ほど`でDBをリセットする必要が出てきました。
DBレコードの消費量はMastodonのVersion、フォローしている人のtoot頻度(TLの流れの速さ)、インスタンスの接続数などによっても変動してくると思われます。
## react-infinite-scroller
```sh
$ yarn add react-infinite-scroller
```
> mastodon/app/javascript/mastodon/features/public_timeline/index.js
```js
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import InfiniteScroll from 'react-infinite-scroll-component';
import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import {
refreshPublicTimeline,
expandPublicTimeline,
} from '../../actions/timelines';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container';
import { connectPublicStream } from '../../actions/streaming';
const messages = defineMessages({
title: { id: 'column.public', defaultMessage: 'Federated timeline' },
});
const mapStateToProps = state => ({
hasUnread: state.getIn(['timelines', 'public', 'unread']) > 0,
});
@connect(mapStateToProps)
@injectIntl
export default class PublicTimeline extends React.PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
multiColumn: PropTypes.bool,
hasUnread: PropTypes.bool,
};
handlePin = () => {
const { columnId, dispatch } = this.props;
if (columnId) {
dispatch(removeColumn(columnId));
} else {
dispatch(addColumn('PUBLIC', {}));
}
}
handleMove = (dir) => {
const { columnId, dispatch } = this.props;
dispatch(moveColumn(columnId, dir));
}
handleHeaderClick = () => {
this.column.scrollTop();
}
componentDidMount () {
const { dispatch } = this.props;
dispatch(refreshPublicTimeline());
this.disconnect = dispatch(connectPublicStream());
}
componentWillUnmount () {
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
}
}
setRef = c => {
this.column = c;
}
handleLoadMore = () => {
this.props.dispatch(expandPublicTimeline());
}
constructor () {
super();
this.state = {divs: divs};
this.generateDivs = this.generateDivs.bind(this);
}
generateDivs () {
const { intl, columnId, hasUnread, multiColumn } = this.props;
const pinned = !!columnId;
moreDivs.push(
<Column ref={this.setRef}>
<ColumnHeader
icon='globe'
active={hasUnread}
title={intl.formatMessage(messages.title)}
onPin={this.handlePin}
onMove={this.handleMove}
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
>
<ColumnSettingsContainer />
</ColumnHeader>
<StatusListContainer
timelineId='public'
loadMore={this.handleLoadMore}
trackScroll={!pinned}
scrollKey={`public_timeline-${columnId}`}
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other instances to fill it up' />}
/>
</Column>
);
setTimeout(() => {
this.setState({divs: this.state.divs.concat(moreDivs)});
}, 500);
}
render () {
return (
<InfiniteScroll
next={this.generateDivs}
hasMore={true}
height={300}
loader={<h4>Loading...</h4>}>
{this.state.divs}
</InfiniteScroll>
);
}
}
```
## リモートフォロー、通知、TLができなくなる問題
リセット前と同じidのアカウント作ったり、またはdbをrestoreすると、リモートフォロー、通知、メインTLなどが流れない現象が確認できました。
この際、`db:restore`後に`db:reset`と`db:migrate`, `redis:cli`にて設定することでこの問題を解消することができるかもしれません。
なお、`db:reset`するためまたid(user)を最初から作り直さなければなりません。
```sh
# heroku run rake db:reset DISABLE_DATABASE_ENVIRONMENT_CHECK=1 -a $app
$ heroku pg:reset -a $app
$ heroku redis:cli -a $app
FLUSHALL
dbsize
quit
$ heroku rake db:migrate -a $app
$ heroku run rake db:seed -a $app
$ heroku run rake assets:precompile -a $app
$ heroku run rake mastodon:daily -a $app
$ heroku run rake mastodon:webpush:generate_vapid_key -a $app
$ heroku config:set ...
# browserでsing up
$ open -a Google\ Chrome https://mstdn.syui.ai
$ heroku run rake mastodon:confirm_email USER_EMAIL=$mail -a $app
$ heroku run rake mastodon:make_admin USERNAME=$USER -a $app
$ heroku config:set SINGLE_USER_MODE=false -a $app
```
その他:
```sh
$ heroku run rake db:reset DISABLE_DATABASE_ENVIRONMENT_CHECK=1 SAFETY_ASSURED=1 -a $app
or
# db:migrate:reset
$ heroku run rake db:migrate:reset DISABLE_DATABASE_ENVIRONMENT_CHECK=1 -a $app
```
## cache clear
```sh
$ heroku run rake mastodon:media:remove_remote -a $app
```
## daily
```sh
$ heroku run rake mastodon:daily -a $app
or
$ heroku run rake mastodon:feeds:clear -a $app
$ heroku run rake mastodon:media:clear -a $app
$ heroku run rake mastodon:users:clear -a $app
$ heroku run rake mastodon:push:refresh -a $app
```
## sing up (admin)
```sh
$ heroku config:set SINGLE_USER_MODE=false -a $app
# まずbrowserでsing upします。そして、以下のコマンドを叩きます。$email="入力したメールアドレス", $username="入力したユーザー名"
$ heroku run rake mastodon:confirm_email USER_EMAIL=$email -a $app
$ heroku run rake mastodon:make_admin USERNAME=$username -a $app
$ heroku config:set SINGLE_USER_MODE=true -a $app
```
## List-of-Rake-tasks
```sh
# https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/List-of-Rake-tasks.md
$ heroku run rake mastodon:push:clear -a $app
$ heroku run rake mastodon:feeds:clear_all -a $app
$ heroku run rake mastodon:feeds:build -a $app
$ heroku run rake mastodon:webpush:generate_vapid_key -a $app
```
## アップデート時にやっといたほうが良いコマンド
```sh
$ heroku run rake db:migrate -a $app
$ heroku run rake assets:precompile -a $app
$ heroku run rake mastodon:daily -a $app
# heroku run rake mastodon:media:remove_remote -a $app
# heroku ps:restart -a $app
# heroku repo:purge_cache -a $app
# heroku run rails c -a $app
# Rails.cache.clear
```
## Domain names and SSL for Heroku
> You should set the Heroku config vars of LOCAL_DOMAIN to your hostname, and LOCAL_HTTPS to "true" as well.
https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Heroku-guide.md
### Herokuでは完全に無料でSSLを使用することは不可能
SSL証明書を登録できるアプリはhobbyからなので、freeでは不可能です。
```sh
$ su
$ cd /etc/letsencrypt/live
$ ls
README chain.pem privkey.pem
cert.pem fullchain.pem
$ heroku certs:add fullchain.pem privkey.pem -a $app --type sni
▸ You need to be running on either Hobby or Professional dynos to be
▸ able to use SNI SSL.
```
> You need to be running on either Hobby or Professional dynos to be
>
> able to use SNI SSL.
### CloudFlareを利用することで見た目上のhttpsは実現可能
CloudFlareのCNAMEを設定することで見た目上のhttpsは実現可能です。しかし、この場合、LOCAL_DOMAINを`mstdn.syui.ai`みたいな形にした上でLOCAL_HTTPSを`false`にしなければならないので、色々な問題が生じる可能性があります。
```sh
# cloudflare
CNAME mstdn appname.herokuapp.com
```
```sh
# heroku
$ heroku config:set LOCAL_DOMAIN=www.example.com -a $app
$ heroku config:set LOCAL_HTTPS=false -a $app
```
### 無料で証明書を手動更新する方法
MastodonのDocsでみると、手動でやれば無料で使えるみたいな書き方なのですが、いまいちよく分かりません。検索してみると、`m/7$`が必要みたいな書き方が多いので、多分ですが、dynoのAppでは登録できないということだと思います。
> [Heroku SSL Dev Center](https://devcenter.heroku.com/articles/ssl#manually-uploading-certificates-and-intermediaries)の記事に記載されている手順に従って、独自の証明書をアップロードしてください。
> Herokuは、有料のdynosで動作するすべてのアプリケーションに対して、無料の自動証明書管理ACMを提供します。ACMを使用すると、Herokuは自動的にSSL証明書をプロビジョニングし、更新します。独自の証明書を手動でアップロードする場合は、この記事の手順に従ってください。
```sh
# 証明書の発行はLet's Encryptを使用
$ brew install certbot
$ sudo certbot certonly --manual
-------------------------------------------------
$ mkdir -p mastodon/public/.well-known/acme-challenge
$ echo "yyyyyyyyyyyyyyyyyyy" > mastodon/public/.well-known/acme-challenge/xxxxxxxxxxxxxx
$ cd mastodon
$ git add ./public/.well-known
$ git commit -m "upload file"
$ git push heroku master
```
これによって公開鍵と秘密鍵が作成されましたので、それをHerokuの方に登録します。登録にはhobby以上の料金設定が必要です。
```sh
$ su
$ cd /etc/letsencrypt/live
$ ls
README chain.pem privkey.pem
cert.pem fullchain.pem
$ heroku certs:add fullchain.pem privkey.pem -a $app --type sni
# Use the --type sni flag if you have an SSL Endpoint add-on on your application already.
$ heroku certs:info
$ curl -vI https://www.example.com
```
> 今日では、新しい無料のSSLサービスと無料のdyno時間を使用するためのより柔軟な方法という2つの重要なアップデートを発表しています。Heroku SSLは今日ベータ版として導入されており、今後数週間から数ヶ月にわたって公開される予定です。6月1日に開始するフレキシブルなdyno時間の開始が予定されています。
2016年5月18日 : https://blog.heroku.com/announcing_heroku_free_ssl_beta_and_flexible_dyno_hours
2016年9月22日 : https://blog.heroku.com/ssl-is-now-included-on-all-paid-dynos
```sh
# これらのコマンドは現在必要ありません
$ heroku labs:enable http-sni -a <your app>
$ heroku plugins:install heroku-certs
# Use the --type sni flag if you have an SSL Endpoint add-on on your application already.
$ heroku certs:add example.crt example.key -a <your app>
```
https://devcenter.heroku.com/articles/ssl#manually-uploading-certificates-and-intermediaries
https://medium.com/should-designers-code/how-to-set-up-ssl-with-lets-encrypt-on-heroku-for-free-266c185630db
### Let's Encrypt
```sh
$ brew install certbot
$ sudo certbot certonly --manual
$ sudo heroku certs:add /etc/letsencrypt/live/plumfeed.com/fullchain.pem /etc/letsencrypt/keys/0000_key-certbot.pem
$ curl -vI https://plumfeed.com
```
http://blog.jonnew.com/posts/heroku-ssl-and-a-free-cert-from-lets-encrypt
```sh
$ openssl genrsa -des3 -out server.pass.key 2048
$ openssl rsa -in server.pass.key -out server.key
$ openssl req -nodes -new -key server.key -out server.csr
```
https://devcenter.heroku.com/articles/acquiring-an-ssl-certificate
https://blog.heroku.com/ssl-is-now-included-on-all-paid-dynos#feedback
### Lets encrypt + dmathieu/sabayon
```sh
$ git clone https://github.com/dmathieu/sabayon.git
$ cd sabayon
$ heroku create letsencrypt-app-for-<name>
$ git push heroku
```
https://blog.odoruinu.net/2017/03/07/setup-lets-encrypt-on-heroku-with-automated-renewal/
### 有料で証明書を自動更新する方法
```sh
# こちらはweb=hobbyです, 有料設定なので気をつけてください
$ heroku ps:resize web=hobby
$ heroku certs:auto:enable
```
https://devcenter.heroku.com/articles/automated-certificate-management
https://devcenter.heroku.com/articles/ssl-endpoint
### Enable streaming on heroku
PR : https://github.com/tootsuite/mastodon/pull/5158
issue : https://github.com/tootsuite/mastodon/issues/1119
```sh
$ heroku config:set NODE_ENV=production -a $app
$ heroku config:set PGSSLMODE=require -a $app
```
### Sub Domain
サブドメインに設定する場合は、`WEB_DOMAIN`を使用すると、`@user@sub.main.com`のような形ではなく`@user@main.com`という形でアドレスがサブを除くトップドメインを利用できる形になります。但し、[ドキュメント](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md)にもあるように様々な問題を発生させます。
```sh
$ heroku config:set WEB_DOMAIN=mstdn.syui.ai -a $app
$ heroku config:set LOCAL_DOMAIN=syui.ai -a $app
$ curl -O mstdn.syui.ai/.well-known/host-meta
# 私の場合はtop domainをgitlabでhostしていますので以下のようにします。
# 具体的にはダウンロードしたhost-metaを同じ場所に置きます。
$ mv syui.gitlab.io/public/.well-known/host-meta
$ cd syui.gitlab.io
$ git add public/.well-known/host-meta
$ git commit -m "add host-meta"
$ git push -u origin gh-pages
```
> .well-known/host-meta
```html
<?xml version="1.0"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
<Link rel="lrdd" type="application/xrd+xml" template="http://mstdn.syui.ai/.well-known/webfinger?resource={uri}"/>
</XRD>
```
https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
> just leave the WEB_DOMAIN key commented out. If you do need it however, youll still have to enter a redirect rule for your root domain that points https://rootdomain/.well-known/host-meta to https://subdomain.rootdomain/.well-known/host-meta. I added a rule on Cloudflare to achieve this, but any approach will do.
https://www.herebedragons.io/running-mastodon
> I'm using a different WEB_DOMAIN (mastodon.develry.be) and LOCAL_DOMAIN (develry.be).
issue : https://github.com/tootsuite/mastodon/issues/2025
### リモートフォロー時の砂時計のマーク
> The requirement for new followers to be approved is something you can enable for your own profile under preferences.
https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/User-guide.md
### OStatusからActivityPubへの移行
> 自動的に承認されはするものの、何らかの理由で「承認」メッセージが受け取れない場合に承認待ち状態が続く可能性があります。承認待ちマーク(砂時計)のクリックでリクエストをキャンセルできるようになっているので、もし上手く承認できない場合はキャンセルしてやりなおしてみてください。
https://hackmd.io/s/H1KSg6ttW
### 1.6から1.5のダウングレード
db:migrateの関係で1.6から1.5のダウングレードは推奨されてなかった気がするのでおすすめしません。私の場合、db:resetしてるのでもはやなんでもできますが(捨てるものがないという意味で)
### LOCAL_HTTPS=false
現在の状況(不都合)をまとめます。これらが通知、リモートフォロー、TLが流れない問題に影響を及ぼしているかどうかは不明。個人的には無関係であり、db:reset, db:restoreなどの実行が影響したものだと考えていますが、複合的要因があるかもしれません。
herokuがhobby以上でないと無料でSSL証明書を登録できませんので、LOCAL_HTTPS=falseにしています。これによって動作不良が起こる可能性を否定できません。
```sh
$ heroku certs:add fullchain.pem privkey.pem -a app --type sni
▸ You need to be running on either Hobby or Professional dynos to be
▸ able to use SNI SSL.
```
### Streaming not working
Webのユーザー画面、管理画面にてStreaming not workingのエラーが起こり、Chromeでは安全でないスクリプトを読み込むを実行しなければ正常に動作しません。これはheroku特有の問題も含みます。
issue : https://github.com/tootsuite/mastodon/issues/1119
### 1.6.1(releases)からmaster(latest)までのアップデート
latest=1.6.1
バージョン(commit)を上げていくと成功するみたいです。
- 1.6.1(releases)からmaster(latest)へのアップデートを行った場合、`heroku rake db:migrate -a $app`で非常に深刻なエラーが出ます。
- master(latest)へのアップデートを完了しました。`db:migration`へのエラーは出ませんでした。`db:reset`後は徐々にバージョン(commit)を上げていくと成功するみたいです。
### 2017/10/09に起こっている不都合
version : 1.6.1, 2.0.0rc1
- リモートフォローができない
- 通知が一切来ない(これはインスタンス内を確認していない、あくまで他インスタンスからのメンション, favを飛ばした形で検証)
- ローカルTL、グローバルTLが流れない
- 通知、フォロー、TLはすべて9月中には動作しているのを確認済み。いつの間にか動作しなくなっており、その後も一切機能しない。
- ログイン画面で`Streaming not working`になる(これは特定のバージョンでは発生しない)。この際、httpsがエラーになり、安全でないスクリプトを読み込まないと画面が表示できない(by Chrome)
- 自動ログインができずパスワード入力などを都度要求される(これは特定のバージョンでは発生しない)
これらは、Rails, Postgres, Mastodonをリセットし、Mastodonに関しては1.6.1のうちの問題が発生していなかった時のバージョンを使用しても解消できなかった。残る手としては、バージョンを1.6.1以外の1.3や1.5などにダウングレードするほかなく、この際、再び問題が生じないよう予め`db:reset`したほうが良さそうです。
### リモートフォローなどができない問題の解決
原因がわかりました。原因はCloudFlareの設定にあったみたいで、SSLをEnableにしているとこの問題が発生しやすいです。
最も単純に動作するのは以下の設定のみを行った場合です。
- LOCAL_HTTPS=false
- LOCAL_DOMAIN=xxx.herokuapp.com
独自ドメインを使ったうえでやる場合は、CloudFlareのSSL設定にあるリダイレクトや強制的に書き換える機能をOFFにします。
### LOCAL_HTTPSなどの挙動
CloudFlareと連携して独自ドメインを使う場合を想定します。挙動にかなり違いが出てくるし、曖昧なので注意が必要です。
最も単純に動作するのは以下の設定のみを行った場合です。
- LOCAL_HTTPS=false
- LOCAL_DOMAIN=xxx.herokuapp.com
xxxはherokuの`$appname`です。
これによって`http://xxx.herokuapp.com`にアクセスできます。
この場合、リモートアドレスは`@user@xxx.herokuapp.com`
しかし、通常は独自ドメインを設定しSSL接続も有効にしたいだろうと思われます。
まず、`cloudflare:ssl=flexible`でやる場合は`LOCAL_HTTPS=false`でないとアクセスできない。`true`ではそもそも表示されない。
しかし、これでも`危険なスクリプトを実行するか?`というChromeのエラーが出てhttpsもエラーが出る。remote addressは`@user@mstdn.syui.ai`というようなsub domainの形をとる。アイコンは正常に表示される。
次に、`cloudflare:ssl=full`で`LOCAL_HTTPS=false`でも上記と同様である。
これを`LOCAL_HTTPS=true`にすると`ssl:full`ではアイコンは表示されないが、remote addressがtop domainの形になる。`@user@syui.ai`
### 画像アイコン等の問題
私は、AWS S3を使っていませんので、別途ストレージを用意する必要があります。
例えば、ユーザーアイコンの扱いについてですが、これは基本的にRails側(Herokuの場合, /var/lib/mastodon/public-system)に保存されると思われます。Heroku Railsのストレージは定期的に再起動し、再起動されるとストレージに保存されたファイルを削除します。
ここで`PAPERCLIP_ROOT_URL`などを使うことによって保存先を指定することができます。
```sh
# インスタンス内で表示するaccounts/の保存先を指定する
$ heroku config:set PAPERCLIP_ROOT_URL=https://syui.gitlab.io/img/mastodon -a $app
# 他インスタンスで参照されるaccounts/の保存先を指定する
$ heroku config:set PAPERCLIP_ROOT_PATH=/app/public -a $app
```
通常は、`/system/accounts/avatars/000/000/001/original/xxx.png`という画像URLが参照される。
`PAPERCLIP_ROOT_URL`を設定することでこれが変更されます。
`heroku run pwd -a $app` : `/app/public`
`root` : `/home/mastodon/live/public`
```sh
$ docker-compose run --rm web rails mastodon:media:remove_remote NUM_DAYS=0
# 画像を再ダウンロードし、画像URLも変更される
$ docker-compose run --rm web rails mastodon:media:redownload_avatars
```
ここで、APIを使ってデフォルトと設定後のユーザー情報を引き出してみました。
> デフォルトの設定
```json
{
"id": "1",
"username": "syui",
"acct": "syui",
"display_name": "",
"locked": false,
"created_at": "2017-10-14",
"note": "<p></p>",
"url": "http://mstdn-syui-2.herokuapp.com/@syui",
"avatar": "http://mstdn-syui-2.herokuapp.com/system/accounts/avatars/000/000/001/original/2fe43582417bfd37.png?1507954652",
"avatar_static": "http://mstdn-syui-2.herokuapp.com/system/accounts/avatars/000/000/001/original/2fe43582417bfd37.png?1507954652",
"header": "http://mstdn-syui-2.herokuapp.com/system/accounts/headers/000/000/001/original/c5968223d7486ab8.png?1507942898",
"header_static": "http://mstdn-syui-2.herokuapp.com/system/accounts/headers/000/000/001/original/c5968223d7486ab8.png?1507942898",
"followers_count": 0,
"following_count": 0,
"statuses_count": 1
}
```
> PAPERCLIP_ROOT_PATH=/app/public, PAPERCLIP_ROOT_ROOT=https://syui.gitlab.io/img/mastodon
```json
{
"id": "1",
"username": "syui",
"acct": "syui",
"display_name": "",
"locked": false,
"created_at": "2017-10-13",
"note": "<p></p>",
"url": "http://syui.ai/@syui",
"avatar": "https://syui.gitlab.io/img/mastodon/accounts/avatars/000/000/001/original/e5415b7a2f69e2cb.png",
"avatar_static": "https://syui.gitlab.io/img/mastodon/accounts/avatars/000/000/001/original/e5415b7a2f69e2cb.png",
"header": "https://syui.gitlab.io/img/mastodon/accounts/headers/000/000/001/original/11bfcd687af9cad0.png",
"header_static": "https://syui.gitlab.io/img/mastodon/accounts/headers/000/000/001/original/11bfcd687af9cad0.png",
"followers_count": 1,
"following_count": 1,
"statuses_count": 8,
"source": {
"privacy": "public",
"sensitive": false,
"note": ""
}
}
```
一度、他インスタンスで画像が認識, ダウンロードされてしまうとなかなかその設定が変更されません(情報によるとリモート時などに更新されるらしい)。`mastodon:daily`などが関係しているのかもしれませんが、よくわかっていません。
私のインスタンスユーザーは他インスタンス上でアイコンが表示されなかった問題があったのですが、以下のコマンドでいけたような気がします。色々やってた時、タイミングよく`mastodon:daily`(他インスタンス上で)などが発動された可能性とかもあるので、まだ確定ではありませんが。
```sh
$ heroku config:set PAPERCLIP_ROOT_URL=https://syui.gitlab.io/img/mastodon -a $app
$ heroku config:set PAPERCLIP_ROOT_PATH=/app/public -a $app
```
私は画像URLを調べてGitLab Pagesの`/public/img/mastodon`にアップロードし、なおかつMastodonのリポジトリにある/publicにaccoutns/をコピーし、git push heroku masterします。
example img url: `accounts/avatars/000/000/001/original/xxx.png`
### loadaverage.orgとのやり取りがうまく行かなかった問題
SSL周りの問題?で他のMastodonインスタンスのやり取りがうまく行かなかった問題を解消すると、今度は、Mastodon間はOKなのに、loadaverage.orgとのやり取りが上手く行かなかったことがありました。
この問題を一旦解決した手順を記述します。
まずドメインを新しいものに変更しました。昔のは色々と紐付けすぎてしまい、あちらが立てば、こちらが立たずという状況になってしまっていました。具体的には、Mastodonで正常な動作を確保したと思ったら、今度は他のサイトがだめになったり、反対に、他のサイトを正常に戻す設定をすると、今度はMastodonに問題が発生したり。
ここで、loadaverageとのやり取りが上手く行った手順を記録します。
- domainを新しくし、top domainをmastodonに割当てる, WEB_DOMAINは設定しない
- heroku deployをしてuser設定をする, アイコンなどを設定する
```sh
LOCAL_DOMAIN=xxx.herokuapp.com
LOCAL_HTTPS=false
$ app="heroku app name"
$ heroku run rake mastodon:confirm_email USER_EMAIL=$email -a $app
$ heroku run rake mastodon:make_admin USERNAME=$username -a $app
```
- envを変更して、updateする, 画像アイコンなどのURLはページからコピーしておく(同じディレクトリ構造でPAPERCLIP_ROOT_URL, PAPERCLIP_ROOT_PATHに置くため)
```sh
$ heroku config:set PAPERCLIP_ROOT_URL=https://syui.gitlab.io/img/mastodon -a $app
$ heroku config:set PAPERCLIP_ROOT_PATH=/app/public -a $app
$ cp -rf accounts mastodn/public/
$ cd mastodon
$ git add .
$ git commit -m "img upload"
$ git push heroku master
# mastodon version=2.0
$ heroku run rake mastodon:media:remove_remote -a $app
$ heroku run rake mastodon:media:remove_silenced -a $app
$ heroku run rake mastodon:daily -a $app
$ heroku run rake db:migrate -a $app
$ heroku run rake assets:precompile -a $app
$ heroku ps:restart -a $app
```
- cloudflareで以下の構成で設定する
```sh
DNS : syui.ai xxx.herokuapp.com
DNS : www xxx.xx.herokudns.com
SSL : Flexible
HTTP Strict Transport Security (HSTS)
Enforce web security policy for your website.
Status: On
Max-Age: 0 (Disable)
Include subdomains: On
Preload: On
No-sniff: On
Always use HTTPS: OFF
Authenticated Origin Pulls: OFF
Opportunistic Encryption: OFF
Automatic HTTPS Rewrites: ON # cachetがfavicon.pngでhttpsエラーを吐くからである
Firewall :
Security Level : Essentially Off
```
## GitLab Pages (Image Storage)
現在、画像ファイルの保存、読み込みを行うサーバーにgitlab.io(具体的には、GitLab Pages)を利用しています。
しかし、このサーバーは反応が遅いので、できれば、GitHubの方に移行するか、若しくはアップロードや管理がしやすい他のサーバーに移行することを考えています。
どうやってアップロードするのかというと、これは手動(自動化は可能)になるのですが、以下の方法が最も適切です。
```sh
host_meta=".well-known//webfinger?resource"
surl=$protocol://$host/$api_url/accounts/1/followers
sjson=`curl -sSL $surl -H "Authorization: Bearer $access_token"`
sbody=`echo $sjson|jq -r '.[]|.acct'`
sn=`echo "$sjson" | jq length`
if [ "$sn" != "0" ];then
sn=$((${sn} - 1))
fi
for ((i=0;i<=$sn;i++))
do
avatar=`echo $sjson|jq -r ".[$i]|.avatar"`
echo $avatar
acct=`echo $sjson|jq -r ".[$i]|.acct"`
url=`echo $sjson|jq -r ".[$i]|.url"|cut -d / -f -3`
output=`curl -sSL -H "Accept: application/json" "$url/${host_meta}=acct:${acct}"|jq -r '.links|.[]|select(.type == "application/atom+xml")|.href'`
curl -sL $output | awk -vRS="</logo>" '/<logo>/{gsub(/.*<logo>|\n+/,"");print;exit}'
done
gurl=$protocol://$host/$api_url/accounts/1/following
gjson=`curl -sSL $gurl -H "Authorization: Bearer $access_token"`
gbody=`echo $gjson|jq -r '.[]|{avatar,acct,url}'`
gn=`echo "$gjson" | jq length`
if [ "$gn" != "0" ];then
gn=$((${gn} - 1))
fi
for ((i=0;i<=$gn;i++))
do
avatar=`echo $gjson|jq -r ".[$i]|.avatar"`
acct=`echo $gjson|jq -r ".[$i]|.acct"`
url=`echo $gjson|jq -r ".[$i]|.url"|cut -d / -f -3`
if ! echo "$gjson"| jq -r ".[]|select(.acct == \"${acct}\")" > /dev/null 2>&1;then
output=`curl -sSL -H "Accept: application/json" "$url/${host_meta}=acct:${acct}"|jq -r '.links|.[]|select(.type == "application/atom+xml")|.href'`
curl -sL $output | awk -vRS="</logo>" '/<logo>/{gsub(/.*<logo>|\n+/,"");print;exit}'
fi
done
```
GitLabへのpushはtokenなどを使って自動化することもできます。
このコードは`.well-known//webfinger?resource`からatomを取得して、atomからavatarのURLを取得します。元となるデータはフォロー、フォロワーを参照します。本来的にはタイムラインも含めるべきですが(リツイートなどで表示する用)、タイムラインを気にしだすとそれを実行する頻度を頻繁にしなければなりませんし、それをやるストレージを用意しようとすると、やはり料金がかかってくると思われます。また、個人インスタンスなのでそれほど必要にはなりません。
## DBのROWS消費が激しい問題
特定の時期に必ずDBのROWSが一気に増加する現象に度々遭遇しています。
それは、インスタンスの接続数が増えてきた時に起こりますが、接続数だけが問題かというとそうではないと考えています。
ここで、以下を実行することによりROWSを抑え、減少させることができました。
- 管理 -> アカウント -> 特定のユーザー -> 完全に活動を停止させる -> 停止から戻す(復帰させる, 再開する)
これを全体的に実行させた後、DBのROWSがかなり減少しました。つまり、これらを実行することで、DBに保存されていたデータが削除される処理が実行されるようです。
と言っても、接続数が50程度でDBのROWSを1日で10,000消費することは通常ありえません。ということは、特定のユーザーないしはbotがDBの消費量を一気に上げているのではないかと考えられます。そういったインスタンスと接続している故なのか、若しくは大手インスタンスにそういったユーザー、ないしはbotが紛れ込んでいるのかは分かりませんが、DBを節約したいならそのへんは考えて、それらを抑制することが重要になってきます。
しかし、完全に活動を停止させると、そのユーザー情報に異変が生じます。具体的には、フォローしていた状態でそれを実行することで、そのユーザーへのフォローが解除され、復活させると再びフォローした状態に戻るのですが、そこで相手方にフォロー通知が届きました(他のMastodonインスタンスで確認, loadaverageのインスタンスでは確認されなかった模様)。
次の問題は、フォロワー欄に相手方が復活しない問題です。これは相手方が一旦こちらをフォロー解除し、再びフォローを実行することで復活する模様。
その他にも完全停止させている状態の時は、通知が来ませんし、またアバターも消えてしまいます。つまり、やり取りが事実上できない状態になってしまうのです。これでは、SNSとしての機能を失ってしまいますので、よくありません。
ということで、DBの消費を抑えつつ、最低限、相手とのやり取りに通知が可能な状態を維持することが目標になります。
ここで、サイレンスという機能を使うことで、通知は可能ですし、サイレンスはフォローしている人以外タイムラインに表示しない機能です。これを利用することで、DBへの消費が抑えられるかもしれません。
現在、DB消費が激しい問題は、/timeline/publicの情報をDBに保存しているからだと考えられます。このtimeline/publicは、当該インスタンスが接続しているフォローしていないユーザーの情報が書き込まれるため、それが原因だと思われます。
ということで、ドメインブロックで一括してサイレントを実行すれば、DBのROWSをある程度抑えることができると思います。
しかし、このサイレント(silence)は一括して実行するコマンドがありません。管理者画面で操作するしかなさそうな感じなので、そのあたりが辛い。
## keybase.io
この度、keybase.ioでsyui.aiのドメインを認証しました。管理者の場合はドキュメントに従ってファイルをアップロードするわけですが、ユーザーの場合は以下のように署名メッセージをtootすればいいでしょう。
```sh
$ keybase sign -m "Hi! I'm @syui@syui.ai on Mastodon."
$ echo "BEGIN KEYBASE SALTPACK SIGNED MESSAGE. kXR7VktZdyH7rvq v5weRa0zkUoNUqZ OqTECTjU1QiHAv4 xzoy3uHNorgXQOG 8NRRqG1wSjpk1ty ibQ3OlfRNMpV1Cf badGHr1vJQZbAuE KVGaeoWXBTeOxLk gu1tp0ukOw2Xkg5 KdKa0J90MTq7C5J LeEdPwW9j1LFKa8 59LLeCUIq6kBIki HhsAMggwl33bcy8 hlNaQLwskpC98fi Yhb4C0iqjFXx9uI NAj6QuM. END KEYBASE SALTPACK SIGNED MESSAGE." | keybase verify
```
## db:resetの弊害
> db:resetしたあとは、一定期間、接続されているインスタンスとのやり取りが不可能になるかもしれない。
一旦、db:resetしてアカウントを作成し直すと通知などが以前接続していた先に届かなくなります。つまり、やり取りができなくなるということですが、これを回避する方法が分かりません。
db:reset前の`generate_vapid_key`を保存しておいて、これを使用することでもしかしたらやり取りができるようになるかもしれません。(結論として無理でした)
```bash
$ heroku config:set VAPID_PUBLIC_KEY=XXXXXXX -a $app
$ heroku config:set VAPID_PRIVATE_KEY=XXXXXXX -a $app
```
その他、`SECRET_KEY_BASE`, `PAPERCLIP_SECRET`や`PROCESS_SCHEDULER_URL`, `OTP_SECRET`なるものがあります。これらを保存しておいて、作り直す際は、これらを使用することで、もしかしたら他インスタンスとの接続において以前の状態を復元できるかもしれません。
ここで特に重要になりそうなのが`SECRET_KEY_BASE`, `PAPERCLIP_SECRET`, `OTP_SECRET` ,`VAPID_PRIVATE_KEY`, `VAPID_PUBLIC_KEY`です。
実験として以下の様なことをやってみました
- 過去に作ったサブのインスタンスを復活させた
- 接続には入っていた。既知のインスタンスにも登録されていた。以前フォローしたことがあるが、解除していた。復活後は普通にやり取りは可能であり、通知も可能だった。
- 再度、そのインスタンスを削除して、再び同じものを作成してみると、こちら側(sub)は通知が来たが、相手には行かなかった。これは、mastodon:dailyなどを実行しても変わらずだった。また、`SECRET_KEY_BASE`, `PAPERCLIP_SECRET`, `OTP_SECRET`を以前のものにしてみるも、やり取りが可能になることはなかった(相手に通知が行くことはなかった)。完全停止させて、復活させてみても同様。
- そして、試しに復活させたインスタンスのアカウントからmastodon.socialに投げてみると、通知が行ったし、以前設定したアイコンが設定されていた。しかし、`SECRET_KEY_BASE`, `PAPERCLIP_SECRET`, `OTP_SECRET`も以前とは違うものなので、通知の可否において、この変数は無関係かもしれない。
間を置いてやり取り可能になっているというのは、期限が切れたものを再取得する際に更新される情報が鍵なのかもしれません。つまり、dailyやsidekiqで期限切れを起こさない限り以前のインスタンス接続情報が有効になるので、新しく作った同じアドレスのアカウントではやり取りができないということです。一度期限切れを起こすことで、初めてやり取りが可能になるのかもしれません。あと、復活させるまでアドレスは完全にない状態で404でした。その辺も関係してくるのだろうか。
http://cryks.hateblo.jp/entry/2017/04/18/125351
### とりあえず実行したコマンド一覧
```sh
heroku run rake db:migrate -a $app
heroku run rake db:seed -a $app
heroku run rake assets:precompile -a $app
heroku run rake mastodon:daily -a $app
heroku run rake mastodon:media:remove_remote -a $app
heroku run rake mastodon:media:remove_silenced -a $app
heroku run rake mastodon:push:clear -a $app
heroku run rake mastodon:feeds:clear_all -a $app
heroku run rake mastodon:feeds:build -a $app
heroku repo:purge_cache -a $app
heroku ps:restart -a $app
```
### とりあえずsidekiqで色々と手動実行する
```sh
# 管理 -> sidekiq
# heroku run rake mastodon:push:refresh -a $app
# スケジュールに設定されている項目を手動実行してみます
```
- メインの方で当該インスタンスアカウントを完全に停止させるとメインの方のフォロワー欄から削除されました。それまではフォロワー欄から消えることはなく、残り続けていました。
- `vapid_key`を同じものにしてもやはりダメだった。
- その後、やり取りができたインスタンスのDBをrestoreしてみたけど、無理だった(userは同じものを作り直した)。
### Cannot run more than 1 Free size dynos.
```sh
$ heroku run rake db:migrate -a $app
Cannot run more than 1 Free size dynos.
$ git status
$ heroku ps
$ heroku ps:stop run.3333
$ heroku run rake db:migrate -a $app
```
## 参考
https://github.com/zunda/mastodon/wiki