部署 Mastodon 实例
1. 什么是 Mastodon
Mastodon 是一个开源的社交软件,在使用上类似于 Twitter,但是在工作机制上却更加接近于电子邮件。Mastodon 是去中心的,只要愿意,任何一个人都可以自行搭建一个 Mastodon 站点。Mastodon 网络(Fediverse,又叫做象毛世界、长毛象宇宙)以“联邦”的形式运行,不同的 Mastodon 站点之间可以互通。而且,因为使用了 OStatus 协议,Mastodon 和 GNU Social 之间也是可以互通的。这样,即使你搭建了一个全新的 Mastodon 示例,也不会陷入无人可关注的窘境。
2. 选择搭建方式
在这里可以找到部署 Mastodon 的官方文档。搭建 Mastodon 有两种方式:
第一种是采用传统的方式,按部就班的配置 PostgreSQL 和 Redis,然后安装 Mastodon 服务端,最后配置 Nginx 以及 HTTPS。第二种则是采用时下较为流行的 Docker 技术。从方便的角度出发,我选择了 Docker。
3. Docker 容器的优势
在国内搭建网站需要备案等繁琐的手续,所以,我直接把这个 Mastodon 实例搭建在了国外的 VPS 上。我所使用的是一个每月开销仅有 5 美元的小型虚拟主机。考虑到小主机不甚稳定,后期人如果有更多的需求,可能需要迁移,如果使用传统方法,那么迁移的时候除了恢复数据库以外,还要把整个安装流程再走一遍,这就带来了很多的问题。
而 Docker 简直就是为了解决这个问题而生的。Docker 分割了应用和数据。其中,应用部分以镜像的形式存在,数据则存储在本地目录当中。然后,可以新建一个容器挂载这个数据目录,并运行镜像当中的应用。这样一来,在迁移服务器的时候,就只需要备份数据库、配置文件还有静态文件就可以了,而镜像部分则完全可以复制过去,开箱即用,这就带来了很大的便利。
这并不是我第一次使用 Docker,我之前也试着使用过 Docker,但是因为感觉使用体验不是很好,于是就放弃了。但是,通过这一次的体验,我发现我之前觉得 Docker 不好并不是 Docker 本身的原因,而是我的使用方式有偏差。Docker 并不适合像虚拟机那样使用,这样的任务应该交给 lxc 这样的容器来完成。Docker 的逻辑是,一个容器只应该办好一件事情,运行一个进程。所以,当一个应用非常复杂,涉及到很多个服务,很多个进程的时候,就应该使用很多个容器共同完成。而 Docker 也确实提供了一个名为 docker-compose
的命令行工具来协助处理此类任务。
4. 搭建流程
我所安装的版本是 Mastodon,大体上还是官方文档。但是,官方文档并非十全十美,如果完全依次行事,可能会遭遇到意想不到的问题。所以,我把整个过程都记录下来,以资参考。
4.1 注册 Mailgun
Mailgun 是用来发注册邮件等东西的。这里不做过多介绍了。在申请完账号之后,Mailgun 会要求你配置 DNS。随后,它会给你分配 SMTP 的用户名、密码。这里需要记下来,后面会用到。
4.2 安装 Docker
我的服务器环境是 Ubuntu 16.04。但是,因为使用 docker 方式安装,其它发行版也大同小异。首先要安装 git、docker 以及 docker-compose。其中,git 只需要 apt-get 即可,而 docker 则不能直接使用官方镜像源的版本(因为太老了),应该直接去官方网站下载最新的稳定版本。运行下列命令即可安装最新的 Docker 社区版:
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install docker-ce
docker-compose 的安装同理,按官方网站的指南安装即可。如下:
sudo curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
4.3 下载 Mastodon
从 Github 拉取最新版本的源代码。
git clone https://github.com/tootsuite/mastodon
cd mastodon
4.4 构建镜像
在构建镜像之前,需要先更改设置,用编辑器打开当前目录下的 docker-compose.yml
文件,把 build .
之前的注释都去掉,另外,镜像中的 volumes
部分的注释也要去掉。否则,在镜像运行完成之后,数据将不会保存。
最后我的 docker-compose.yml
如下,可供参考。
version: '3'
services:
db:
restart: always
image: postgres:9.6-alpine
networks:
- internal_network
### Uncomment to enable DB persistance
volumes:
- ./postgres:/var/lib/postgresql/data
redis:
restart: always
image: redis:4.0-alpine
networks:
- internal_network
### 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:
# - internal_network
#### Uncomment to enable ES persistance
## volumes:
## - ./elasticsearch:/usr/share/elasticsearch/data
web:
build: .
image: tootsuite/mastodon
restart: always
env_file: .env.production
command: bundle exec rails s -p 3000 -b '0.0.0.0'
networks:
- external_network
- internal_network
ports:
- "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:
- external_network
- internal_network
ports:
- "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:
- external_network
- internal_network
volumes:
- ./public/packs:/mastodon/public/packs
- ./public/system:/mastodon/public/system
networks:
external_network:
internal_network:
internal: true
而这几个镜像的配置文件则是当前目录下的 .env.production
,然而在默认状态下这个文件是不存在的,只有一个 .env.production.sample
,因此需要重命名。
cp .env.production.sample .env.production
此时暂时不需要编辑这个文件,因为后面会有脚本来处理里面的设置。
此外,在构建镜像之前,需要处理一个问题。public
目录下面有两个无效的软链接,这是因为此时静态文件还没有生成。所以,需要先 hack 一下。
cd public/assets/
touch 500.html
touch sw.js
cd ../../
最后运行 docker-compose build
构建镜像。
4.5 配置
在配置之前,需要解决数据权限的问题。Mastodon 的 Dockerfile 写明了镜像中的应用在运行时以拥有“991”这个 UID 的用户运行。所以,需要更改 public
目录的权限。
chown -R 991:991 public/
然后把刚才的那两个 hack 出来的垃圾文件删掉
rm public/assets/500.html
rm public/assets/sw.js
接着就可以生成配置文件了。
docker-compose run --rm web rake mastodon:setup
这里会让你填写域名之类的,只要按照提示操作即可。其中,需要填写邮箱设置,就把前面 Mailgun 那里的设置复制过来就行了。最后,它会让你生成一个管理员账号,记得把密码复制下来。
配置完成之后,还可以预编译静态文件。
docker-compose run --rm web rake assets:precompile
4.6 完成
运行 Mastodon 吧~
docker-compose up -d
5. Nginx 和 HTTPS
当然到这里还没结束,为了用户的安全,Mastodon 要求服务器开启 HTTPS。曾经,一个 TLS 证书是很昂贵的,价格高达 200 元,但是所幸我们现在有 Let’s Encrypt 这种良心 CA,可以免费拿到证书。最近,Let’s Encrypt 还新增了域名通配符的支持。或许后面我会写一篇文章介绍一下 Let’s Encrypt。但是这里不再赘述。
至于 Nginx 的配置,我的推荐是照搬 Mastodon 的官方文档。我认为其安全性还是达标的。配置文件如下:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name example.com;
root /home/mastodon/live/public;
# Useful for Let's Encrypt
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_protocols TLSv1.2;
ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
keepalive_timeout 70;
sendfile on;
client_max_body_size 0;
root /home/mastodon/live/public;
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";
location / {
try_files $uri @proxy;
}
location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri @proxy;
}
location /sw.js {
add_header Cache-Control "public, max-age=0";
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://127.0.0.1:3000;
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 /api/v1/streaming {
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 http://127.0.0.1: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;
}
error_page 500 501 502 503 504 /500.html;
}
当然,要把里面的 example.org
改成自己的域名。
6. 服务器备份
Mastodon 的服务器备份包括三个步骤:备份静态文件、备份数据库、备份配置文件。
6.1 备份数据库
Mastodon 使用 PosgreSQL 作为数据库。PostgreSQL 提供了一个名为 pg_dump
的工具用作备份。使用方式如下:
docker-compose exec -T db pg_dump -U postgres postgres > backup.sql
然后把 backup.sql 下载下来,或者上传到 Google Drive 之类的云存储空间里。
6.2 备份静态文件
需要备份的静态文件位于 public/system
主要是用户上传的图片这些。直接用 rsync 备份即可,可参考这里。
6.3 备份配置文件
这个步骤只需要进行一次,把 .env.production
文件复制下来就行了。
7. 其它
服务在日常使用中还可能遇到升级、迁移等问题,这些我还没有实际操作过。日后也许会更新在这里。
8. 结束
至此,服务其已经配置完毕,打开浏览器,输入用户名和密码,开始使用吧~
本博客不设评论功能,但是欢迎给我发送电子邮件(nebula_moe@protonmail.com
)。

本博客所有作品,如果没有特殊说明,均遵从CC版权协议,采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© 2019 奇想齋 ― Powered by Jekyll and Textlog theme