mastodon

自从老马被迫收购 Twitter 后,操作不断,改名 X 后更是在成为微信的路上一去不复返,虽然我只是偶尔上 Twitter 看看猫猫狗狗的萌图,但感觉老马迟早不给免费用户玩。于是未雨绸缪,搭建一个自己用。以前分享过使用 Docker 搭建去中心化的微博客平台 Misskey,这次又探索了一些类似平台,如 SoapboxMinds 以及基于 Misskey 的 firefish 等,迫于兼容性和文档考虑,最后还是准备使用 Mastodon

虽然网上有很多详细的教程,但大多是很早以前的了。我在搭建时可以说是充满曲折,这里也记录下来,希望对遇到了同样问题的朋友有帮助。

* 演示安装环境为 Debian 12,使用 root 用户,采用预编译的官方 Docker 镜像通过 Docker Compose 部署。

准备工作

Docker 和 Docker Compose 的安装与基础使用可以参考文章 Debian 系统安装 Docker 教程,不建议在中国大陆的服务器上部署 Mastodon。

在合适的路径下创建目录:

mkdir -p /home/mastodon && cd /home/mastodon

修改 docker-compose.yml 配置文件

wget https://raw.githubusercontent.com/mastodon/mastodon/main/docker-compose.yml
vim docker-compose.yml

注释掉所有镜像(web, streaming, sidekiq)的编译命令 build: .,并修改你想要的镜像版本,如 image: ghcr.io/mastodon/mastodon:v4.2.1,如果你想要最新版本则设置为 image: ghcr.io/mastodon/mastodon:latest。如果你需要全文搜索功能,则将 es 服务整块取消注释。

修改 .env.production 配置文件

这里有手动和交互式向导两种方式配置环境。但如果直接使用自带向导配置可能会在最后一步设置管理员账户的时候弹出一个 Redis 连接的错误:

ERROR -- : RedisCacheStore: read_entry failed, returned nil: Redis::CannotConnectError: Error connecting to Redis on localhost:6379 (Errno::ECONNREFUSED)

失败几次后发现可能的原因是在向导配置时没有写入参数导致找不到 Redis Host。有两个办法解决,先手动设置 REDIS_HOST 后再通过交互式命令配置,或者先不要设置管理员账户,之后用 tootctl 工具创建,这里我选择前者。

获取并修改 .env.production 配置文件:

wget https://raw.githubusercontent.com/mastodon/mastodon/main/.env.production.sample -O .env.production
vim .env.production

提前修改部分参数,如果在生产环境中,这里建议修改 DB_USER 设置一个 Mastodon 数据库专用用户(例如 mastodon)并设置 DB_PASS 密码,下例为了方便使用 Postgres 数据库的默认账户 postgres 进行演示:

# Federation
# ----------
# This identifies your server and cannot be changed safely later
# ----------
LOCAL_DOMAIN=<你的域名>

# Redis
# -----
REDIS_HOST=mastodon-redis-1
REDIS_PORT=6379
REDIS_URL=redis://@mastodon-redis-1:6379

# PostgreSQL
# ----------
DB_HOST=mastodon-db-1
DB_USER=postgres
DB_NAME=mastodon_production
DB_PASS=
DB_PORT=5432

...

配置 Mastodon

运行配置命令:

docker compose run --rm web bundle exec rake mastodon:setup

第一次拉取镜像时可能要花些时间(下载 1G+),耐心等待。这里注意在配置时数据库和 Redis 的相关参数和前面文件中的保持一致。

mastodon-setup

最后一步完成后记下输出的管理员密码。并将上方打印的 .env.production 相应参数手动覆盖到文件中,主要是 SECRET_KEY_BASE, OTP_SECRET, VAPID_PRIVATE_KEY, VAPID_PUBLIC_KEY 这几个,完整的参数列表和作用请查阅文档

启动 Mastodon

修改 public 目录权限:

chown -R 991:991 public

启动 Mastodon 服务:

docker compose up -d

配置 Nginx

参照官方模版,修改文件中的域名和 root 路径,并将所有的 try_files $uri =404; 替换为 try_files $uri @proxy;

如果你幸运的话此时可以正常打开网站使用了。

但我到这里折腾了一个通宵也没成功,打开网站一片空白,所有静态资源无限报 net::ERR_HTTP2_PROTOCOL_ERROR 200 (OK) 错误,以前在其它程序也遇到过,当时参考 Stack Overflow 上一篇帖子关闭 proxy_max_temp_file_size 缓存限制就好了。

server {
  ...
  ...
  gzip off;
  proxy_max_temp_file_size 0;
  location / {
    proxy_pass http://127.0.0.1:3000/;
    ...

然而这里却没有起作用,网上也搜不到相关的错误。几经放弃,最后又复盘了下,能建立链接但是资源请求失败,肯定是反代那里出了问题,终于在尝试取消反代缓存后居然成功进入了网站界面。

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 $scheme;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

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

    # proxy_cache CACHE;
    # proxy_cache_valid 200 7d;
    # proxy_cache_valid 410 24h;
    # proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    # add_header X-Cached $upstream_cache_status;

    tcp_nodelay on;
}

接着又经过无数次探索和尝试,最后发现万恶之源在 proxy_cache_path 上面。根据 Nginx 文档的说法:

The directory for temporary files is set based on the use_temp_path parameter (1.7.10). If this parameter is omitted or set to the value on, the directory set by the proxy_temp_path directive for the given location will be used. If the value is set to off, temporary files will be put directly in the cache directory.

临时文件的目录默认为 on,Nginx 会先把要缓存的文件放到临时存储区,如果为 off,则会将缓存文件直接写入指定的 cache 文件中,而不使用 temp_path 指定的临时存储路径。

可能就是这里读取缓存文件时出现了玄学问题导致我一直没成功,在尝试添加 use_temp_path=off 后终于完美运行。

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g use_temp_path=off;

mastodon-setup

运维管理操作

这里记录一些常用的使用 Docker 部署的 Mastodon 服务器运维管理操作。

使用 tootctl 管理

管理 Mastodon 推荐通过内置的 tootctl 命令进行,Docker 部署的 Mastodon 最直接的方式可以使用 docker exec 命令管理:

docker exec mastodon-web-1 tootctl <command>

例如通过使用 tootctl 创建一个 id 为 newuser 的普通用户:

docker exec mastodon-web-1 tootctl accounts create \
  newuser \
  --email newusr@example.com \
  --confirmed

如果是一些需要交互的设置,可以进入容器后再在执行命令:

docker exec -it mastodon-web-1 /bin/bash

此时,你可以配合 crontab 执行定时任务来自动化管理,例如每天凌晨 3 点定时清理本地缓存的其它实例媒体文件:

0 3 * * * docker exec mastodon-web-1 tootctl media remove --days=7
0 3 * * * docker exec mastodon-web-1 tootctl media remove-orphans
0 3 * * * docker exec mastodon-web-1 tootctl statuses remove --days=90
0 3 * * * docker exec mastodon-web-1 tootctl preview_cards remove --days=180

其中:

  • tootctl media remove :清除缓存超过指定天数的外站媒体附件
  • tootctl media remove-orphans :移除不属于任何媒体附件的文件
  • tootctl statuses remove :从数据库中删除超过指定天数未被引用的嘟文
  • tootctl preview_cards remove : 移除超过指定天数的本地预览卡片缩略图

迁移服务器

如果旧服务器正常运行的话,最简单的方法是直接迁移 Docker 容器。这里主要结合官方文档,给出旧服务器忘记续费/rm -rf/爆炸后,只有备份文件的情况下的恢复示例 为了验证流程有效性,我直接把数据库删了 QAQ

你平时可以通过下面的命令导出数据库文件,和 .env.production 文件以及媒体文件(public/system)一起使用 rsync 或者 rclone 定时自动备份到安全的地方。

docker exec mastodon-db-1 pg_dump -U postgres -Fc mastodon_production > /path/to/db-backup.dump
  1. 恢复数据库

在新服务器上将数据库备份文件、docker-compose.yml.env.production 文件复制到你的项目路径下。注意不能和部署新实例时一样运行向导配置。

进入你的项目路径,启动数据库容器:

docker compose up -d db

使用 docker ps 找到该容器 id,拷入数据库文件之后进入容器创建一个新的空白(UTF8/en_US.utf8)数据库:

docker cp ./db-backup.dump <container id>:/tmp/db-backup.dump
docker exec -it <container id> /bin/bash
su - postgres
createdb -T template0 mastodon_production

* 如果你此前使用了其它账户管理数据库,这里还需要新建相应的数据库用户,假设该用户为 mastodon,密码和 .env.production 中一致:

psql
CREATE USER mastodon WITH PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE mastodon_production TO mastodon;
\q

导入数据库,注意修改 -U--role 参数相应的用户名:

pg_restore -U mastodon -n public --no-owner --role=mastodon \
  -d mastodon_production /tmp/db-backup.dump

最后退出并停止容器。

  1. 导入媒体文件

直接将备份的 /public/system/ 目录上传到项目路径下并修改权限即可:

chown 991:991 -R ./public

如果你没有备份外站用户的缓存(例如头像、头图等),之后会出现 404,可以刷新所有用户缓存:

docker compose run --rm web bin/tootctl accounts refresh --all

媒体附件同理可以通过 tootctl media refresh 刷新。

  1. 重建主页时间流
docker compose run --rm web bin/tootctl feeds build

等待完成后就可以启动 Mastodon 服务了:

docker compose up -d

如果你准备开放实例供他人注册使用的话,需要配置和优化的地方还有很多,连接中继、云端存储、定时备份、性能优化等等,我这里由于服务器性能限制,暂时不打算开放注册,就不再研究了。

如果你认为这篇文章还不错,可以考虑支持作者