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 改个名字,必须叫 stack.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,啥啥都用不了,而且是在内网,也搞不到证书,后续的反向代理到公网也没办法实验。而且即使代理到公网上,好像也会遇到证书的问题?那就……作罢!删虚拟机!不玩了!

参考资料


评论

发表回复

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