Docker 部署 Mastodon(失败了)

Mastodon,中文名字叫「长毛象」,可以理解为自托管的 Twitter/微博。自从马斯克收购了 Twitter,施行了一系列匪夷所思的激进政策后,一大群人离开了 Twitter,走进了自托管的 Mastodon 的世界。

看到可以自托管,那么,不如自己搭一个试试?

哦,我失败了……下面是我做过的尝试。

阅读更多

构想

  1. 由于 Mastodon 需要的内存有点多(搭好后啥都不干,内存占用 2G),搭在 1C1G 的 LightSail 小鸡上是不太可能了。所以要么搭在小主机上(N6005+16G 内存),要么搭在 NAS(J4125+16G 内存)上。
  2. 搭好后,用 FRP 转发到公网,或者用 TailScale 组个内网,想办法转发出去
  3. 肯定是全 Docker 方案啦~

步骤

docker-compose 文件

Mastodon 的 Git 里面有一个 docker-compose.yaml 文件可以用。拉下来,可以看到整个项目分成这几部分:

  1. 数据库类:postogres 数据库和 redis 缓存
  2. 前端:web
  3. API: streaming
  4. 后台任务:sidekiq

我们对这个文件做修改:

  1. 临时地,给 postgres 加个用户名和密码。不然后面我们得手动加个用户……就懒到极致吧。在 dbenvironment 里面,将原来的 POSTGRES_HOST_AUTH_METHOD 给干掉,然后加上 POSTGRES_DBPOSTGRES_USERPOSTGRES_PASSWORD 三个环境变量就好了。
  2. 删除 web 、 streaming 和 sidekiq 里面的 build,这样就可以用预编译的东西了
  3. 给所有容器都分配个名字吧,就在每个容器里面加个 container_name 。这样好处是后面填数据库地址、redis 地址,可以直接填这个名字,比较方便
  4. volumes,其实就一个,映射到了容器里面的 /mastodon/public/system 目录。这个目录是用户上传的文件,如果后面想用 S3 的话就把这个映射给干掉
  5. 为了简单,就先不开 elastic search 吧……直接删掉

安装 Mastodon

好了,前面的编排文件写好了,但是在「安装」这一步(其实是生成一些 Token 啊,Salt 啊之类的配置),我们使用命令行和 Portainer 有那么一点点不同。

使用命令行安装

如果你比较习惯于用命令行,那么也可以直接跑下面的指令,临时运行个容器来跑安装过程:

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

它会问你好多东西,按照你想的填就是了。过程中有几个东西比较重要:

  1. 它会问你是不是要保存配置,选「是」,然后它会把所有配置都打在屏幕上,我们需要手动复制下来
  2. 新创建个文件(例如叫 .env.production ,反正和你编排文件里面写的 env_file 相同就好了),将所有东西复制到这个文件里。
  3. 之后会问要不要创建数据库,选「是」
  4. 之后会问你要不要创建管理员用户。这里直接创建的话会有 Bug(此时会无视你前面填写的 redis 地址,强制使用 localhost,导致报错),所以暂时选 否,后面再用其他方法创建

这些搞完之后,可以执行 docker-compose down 先关掉所有容器,然后再 docker-compose up -d 运行所有容器。

最后使用下面语句创建管理员用户(这里面的 web 是 web 容器的名字。如果你制定了名字,就用你指定的;如果没指定,容器可能叫 mastodon_web_1 或者 web_1):

docker exec web tootctl accounts create USERNAME --email EMAIL --confirmed --role Owner

安装好后,输入 curl http://127.0.0.1:3000/health 看看是不是能看到「OK」字样。

使用 Portainer 安装

由于 Portainer 会一下将所有容器都启动起来……但你没那些配置还启动不起来容器,这就产生了先有鸡还是先有蛋的问题。所以我们需要临时写一个「简化版」的编排文件,。

首先,保留 dbredis 这两段, 其他全删除。然后把 web 改写成这样:

  web:
    image: tootsuite/mastodon
    restart: always
    environment:
      - "RAILS_ENV=production"
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; tail -f /etc/hosts"
    networks:
      - external_network
      - internal_network

整个 stack 启动起来后,去 web 的 console 里面执行下面这条指令,就能开始「安装」过程:

bundle exec rake mastodon:setup

其他的和上面一样,同样是获取到一大堆环境变量。直接复制出来,然后回到写 yaml 的那个地方,下面有 environment,点击高级,将这一大堆环境变量贴进去。

然后将 yaml 文件替换为正常的 yaml。这里还有个比较重要的点:需要在上面 yaml 编辑器里面将 web 、 streaming 和 sidekiq 里面的 env_file 改个名字,比如叫 mastodon.env 。如果使用默认的.env.product 的话,Portainer 会给你发脾气,说没有这个文件。


好了,就到这里了!我遇到的问题是,直接访问 http://127.0.0.1:3000 各种报 403 错误,连明明确实存在的静态文件也会报 403;访问 http://127.0.0.1:4000 的话会报没鉴权(符合预期)。所以我就没辙了。下面都是我做过的其他尝试,都没成功。各位如果有成功的,求借鉴一下经验。

设置正向代理

你看到的 Mastodon 设置里面,都会把 Nginx 和 SSL 放为重点中的重点,大篇大篇都在写如何高 SSL。查了 Issues,就是说,如果没有 SSL 的话,那么一些高级功能,例如服务端主动推送啥的都会不能用。所以,尽量配个 SSL 吧。但不配 SSL 其实吧也能用,体验差点而已。

那么,配置大致 SSL 有两个方案:第一个是用 Nginx 做代理,然后用 Certbot 或者 acme.sh 来搞证书;另一种是直接用 Caddy 来做代理和搞证书。下面两个方案都写一下:

Nginx

Mastodon 的 repo 里面有个 nginx 的 demo。拉下来,然后改改:

  1. 它说路径是 /home/mastodon/live/public ,但实际上我们需要将中间的 live 都去掉。哦对了,这个路径前面说过,是 Mastodon 的用户上传文件的路径,如果你用 S3 的话,完全不必操心这个
  2. 注释里面说,如果用 Docker 部署,那么需要将所有的 try_files $uri =404; 全部改成 try_files $uri @proxy;
  3. 我自己比较强迫症,所以将 assetsavatarsemoji 等目录全都写在了一起……你看嘛,除了路径不同,其他配置都一模一样的,而且用的都是正则,所以合并写在一起,可以立省好几十行:
  location ~ ^/(assets|avatars|emoji|headers|packs|shortcuts|sounds|system)/ {
    add_header Cache-Control "public, max-age=2419200, must-revalidate";
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
    try_files $uri =404;
  }

然后呢,去改一下原来的 docker-compose.yaml ,把 Nginx 加进去

  nginx:
    image: nginx:alpine
    container_name: nginx
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./data/nginx:/etc/nginx/conf.d:ro
      - ./logs/nginx:/var/log/nginx
      - ./data/acme.sh/deploy:/etc/letsencrypt:ro
      - ./data/mastodon/public:/home/mastodon/public:ro
    restart: always

SSL

不知道为啥,官方教程、其他教程里面都推荐用 CertBot,毕竟是 LetsEncrypt 项目的亲儿子。但我是 N 年前从 CertBot 叛逃到 acme.sh 的,感觉真香。

那,顺带的,再在 docker-compose.yaml 里面加上个 acme.sh 呗:

  acme.sh:
    image: neilpang/acme.sh
    container_name: acme.sh
    restart: always
    command: daemon
    volumes:
      - ./data/acme.sh/internal:/acme.sh
      - ./data/acme.sh/deploy:/etc/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock

那这样差不多就成了。整个项目用 docker-compose up -d 启动起来,然后去申请证书就好了。

Caddy

上面选正向代理的时候比较纠结:Nginx 还需要我自己去搞证书,还是个内网的证书,多麻烦。那,不如直接让 Caddy 代劳,同时完成反代和证书这两个事儿吧。

首先你需要写一份 Caddyfile。在 Mastodon 的一个 PR 里面找到了一份十分简洁的 Caddyfile。有胆量想 Merge 到官方 Repo 的代码,应该质量还不错。但同样的,也需要把路径里面的 live 给去掉。

然后是正常搞 Caddy。在 docker-compose.yaml 里面加上:

  caddy:
    image: caddy:<version>
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./data/Caddy/Caddyfile:/etc/caddy/Caddyfile
      - ./data/Caddycaddy_data:/data
      - ./data/Caddycaddy_config:/config
      - ./data/mastodon/public:/home/mastodon/public:ro

LinuxServer 的镜像

就是说,LinuxServer 这个组织,自己将一些常见的应用重新包装了一下,做成了 Docker 镜像。有些重打包打得还不错。

可以看这个 https://hub.docker.com/r/linuxserver/mastodon ,它是将 Mastoodn 的三个应用合到了一起,甚至把 Nginx 也配在了镜像里,你只要再配置个 Postgres 和 redis 就好了。至于环境变量,你可以按照它的 Readme 里面说的,全部搞成环境变量写在 docker-compose.yaml 里面,也可以像官方教程一样搞 .env 文件。反正除了 docker-compose.yaml 里面镜像个数减少了一点之外,其他也没啥变化。

启动起来后,依然是啥啥都是 403 Forbidden,但是比上面官方原版的有一点好是,它静态文件是可以正常工作的。镜像里面似乎比较乱,竟然还有个 PHP……emm,不知道该说啥好。


反正就是,啥啥都 403 Forbidden,啥啥都用不了,而且是在内网,也搞不到证书,后续的反向代理到公网也没办法实验。而且即使代理到公网上,好像也会遇到证书的问题?那就……作罢!删虚拟机!不玩了!

参考资料


已发布

分类

作者:

标签

评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注