关于ruby:使用-Mastodon-搭建个人信息平台前篇

47次阅读

共计 23078 个字符,预计需要花费 58 分钟才能阅读完成。

本篇文章是应用 Mastodon 搭建个人信息平台的第一篇内容,我将聊聊在容器环境中搭建 Mastodon 的一些细节。

同时,这篇文章或者你可能找到的为数不多的对于如何在容器环境中搭建和优化 Mastodon 服务的内容。

写在后面

随着折腾的零碎越来越多,我开始冀望有一个中央可能将这些零碎中的音讯进行集中的出现,让我可能疾速清晰的理解到有什么乏味的陈腐的、重要的事件产生了,以及让我可能通过更简略的形式对已有零碎中的数据进行疾速的查问,以及记录一些忽然呈现的想法。

我认为以时间轴为线索的 Feed 流模式的信息展现,配合和各种“虚构利用”和 Bot 的对话形式或者可能解决我这个阶段的诉求。交互简略间接、交互操作层级也浅,在少数查问和记录场景下,我只须要输出内容,按下回车就能拿到我想要的数据,而不用关上具体的利用的页面,而后再一步一步、一步一步的操作。

在以往工作和生存中,其实多多少少也有应用过一些蕴含了交互或者性能和我诉求有交加的工具,比方:在新浪云工作应用的 TeamToy、在淘宝时应用的 Redmine 和阿里门户、美团时应用的大象、之后应用的 Slack、企业微信、学城等等。

不过这类计划少数都是外部或者 SaaS 化的计划,在集体应用场景下,尤其是联合各种 HomeLab 零碎,我更心愿它是一个私有化的服务。

对于新增“实体”,我比拟克服,所以所以在此之前的摸索过程中,我对 Phabricator、Confluence、WordPress、Dokuwiki、Outline 等各种我之前比拟相熟的零碎都进行过了一些考察和简略的二次开发,发现尽管可能解决一部分问题,然而交互和体验上总感觉不是那么难受。因为这些工具或多或少都是基于合作登程,或者基于内容整顿登程,而不是信息汇聚和展现。我须要一个即便一个人应用也能很爽的计划。

于是,我开始彻底尝试切换思路,寻找一个上文中提到的,以时间轴为信息展现线索,可能和工具中的 Bot 互动,来记录我的想法、将各种我关注的事件实时汇聚到工具中,可能以简略的命令和办法查问各种零碎中已有的数据。最终,我抉择了 Mastodon,一个两年前我就曾经折腾过一阵的“Twitter / Weibo Like”的产品。

在开始折腾之前,咱们先来聊聊它的技术架构。

技术架构

Mastodon 的技术架构属于比拟经典的 Web 架构,次要的性能组件有:前端利用(React SPA)、利用接口(Ruby Rails6)、推送服务(Node Express + WS)、后台任务(Ruby Sidekiq)、缓存和队列(Redis)、数据库(Postgres),以及可选的全文索引(Elasticsearch 7)形成。

除此之外,反对应用匿名网络通讯的形式和互联网上其余不同的社区实例通信,替换社区已公布内容,来实现其分布式社区的构想。不过这个性能不在本文范畴之内,而且非常简单,就不啰嗦开展了。

根底服务筹备

在折腾利用之前,咱们先实现利用对于根底服务的依赖设施的搭建。先来聊聊网络布局。

搭建利用网关,进行网络布局

和以往利用一样,咱们应用 Traefik 作为服务利用网关,让利用能够应用服务注册的形式动静地接入 Traefik。并且应用 Traefik 提供 SSL 装载、根底的 SSO 鉴权等。

如果你还不理解 Traefik,能够浏览之前的内容进行学习和理解。

我心愿 Mastodon 各个组件在可能通信、必要的服务可能应用 Traefik 进行服务注册,提供 Web 拜访的前提下,还能和主机上其余的容器服务在网络层面互相隔离。

出于下面的思考,咱们能够执行命令,创立一个额定的虚构网卡进行组件之间的通信买通:

docker network create mastodon_networks

搭建数据库:Postgres

官网配置文件中,对于数据库的定义是这样的:

version: '3'
services:

  db:
    restart: always
    image: postgres:14-alpine
    shm_size: 256mb
    networks:
      - internal_network
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
    volumes:
      - ./postgres14:/var/lib/postgresql/data
    environment:
      - "POSTGRES_HOST_AUTH_METHOD=trust"

尽管也能应用,然而数据库运行之后,咱们会收到程序到一些运行正告。

********************************************************************************
WARNING: POSTGRES_HOST_AUTH_METHOD has been set to "trust". This will allow
         anyone with access to the Postgres port to access your database without
         a password, even if POSTGRES_PASSWORD is set. See PostgreSQL
         documentation about "trust":
         https://www.postgresql.org/docs/current/auth-trust.html
         In Docker's default configuration, this is effectively any other
         container on the same system.

         It is not recommended to use POSTGRES_HOST_AUTH_METHOD=trust. Replace
         it with "-e POSTGRES_PASSWORD=password" instead to set a password in
         "docker run".
********************************************************************************

在利用运行过程中,数据库终端会一直地积攒一些申请日志、后台任务执行后果日志输入,最终会产生一个十分大的利用日志文件。在极其的状况下,甚至可能因而将磁盘占满,影响整台服务器上其余利用的失常运行。

所以,我结合实际情况,我对下面的配置做了一些简略调整:

version: '3'
services:

  db:
    restart: always
    image: postgres:14-alpine
    shm_size: 256mb
    networks:
      - mastodon_networks
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 15s
      retries: 12
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./data:/var/lib/postgresql/data
    environment:
      - "POSTGRES_DB=mastodon"
      - "POSTGRES_USER=mastodon"
      - "POSTGRES_PASSWORD=mastodon"
    logging:
      driver: "json-file"
      options:
        max-size: "10m"

networks:
  mastodon_networks:
    external: true

将下面的内容保留到 postgres 目录的 docker-compose.yml 文件中之后,咱们应用 docker-compose up -d 启动服务,稍等片刻,应用 docker-compose ps 查看利用,能够看到服务运行失常。

# docker-compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
postgres-db-1       "docker-entrypoint.s…"   db                  running (healthy)   5432/tcp

这部分的配置和代码曾经上传至 GitHub,有须要能够自取:https://github.com/soulteary/Home-Network-Note/tree/master/example/mastodon/postgres

搭建缓存和队列服务:Redis

默认的 Redis 启动会在 30 秒之后提供服务,对于咱们而言有一些久。为了让 Redis 开始提供响应的工夫更快,我同样对官网配置中的内容进行了简略的调整:

version: '3'
services:

  redis:
    restart: always
    image: redis:6-alpine
    networks:
      - mastodon_networks
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 15s
      retries: 12
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./data:/data
    logging:
      driver: "json-file"
      options:
        max-size: "10m"

networks:
  mastodon_networks:
    external: true

将配置保留到 redis 目录的 docker-compose.yml 后,咱们应用 docker-compose up -d 启动服务,稍等片刻,应用 docker-compose ps 查看利用,能够看到服务运行失常。

# docker-compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
redis-redis-1       "docker-entrypoint.s…"   redis               running (healthy)   6379/tcp

这部分的配置和代码也曾经上传至 GitHub,有须要能够自取:https://github.com/soulteary/Home-Network-Note/tree/master/example/mastodon/redis

搭建全文检索:Elasticsearch

这个组件对于 Mastodon 是可选的,有几个状况下你可能不须要应用 ES:

  • 你的机器资源十分缓和,启用 ES 将额定的占用 500MB~1GB 的内存
  • 你的站点内容和用户数并不多
  • 你的搜寻次数十分无限
  • 你冀望应用资源和性能更高的检索计划

在 2018 年的 PG CONF EU 上,Oleg Bartunov 已经做过一个分享,对于应用 Postgres 在全文检索场景的应用,感兴趣能够自行理解。

当然,出于对官网抉择的尊重,咱们还是简略开展一下 ES 的搭建和应用。同样基于官网配置进行简略调整,能够实现一个新的根底编排文件:

version: '3'
services:

  es:
    restart: always
    container_name: es-mastodon
    image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
    environment:
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - "cluster.name=es-mastodon"
      - "discovery.type=single-node"
      - "bootstrap.memory_lock=true"
    networks:
      - mastodon_networks
    healthcheck:
      test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"]
      interval: 15s
      retries: 12
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./data:/usr/share/elasticsearch/data:rw
    ulimits:
      memlock:
        soft: -1
        hard: -1
    logging:
      driver: "json-file"
      options:
        max-size: "10m"

networks:
  mastodon_networks:
    external: true

不过,如果咱们将下面的编排文件保留,并尝试启动服务,会遇到一个经典的问题,目录权限不正确,服务无奈启动:

"stacktrace": ["org.elasticsearch.bootstrap.StartupException: ElasticsearchException[failed to bind service]; nested: AccessDeniedException[/usr/share/elasticsearch/data/nodes];",
"at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:174) ~[elasticsearch-7.10.2.jar:7.10.2]",
...

ElasticsearchException[failed to bind service]; nested: AccessDeniedException[/usr/share/elasticsearch/data/nodes];
Likely root cause: java.nio.file.AccessDeniedException: /usr/share/elasticsearch/data/nodes
    at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:90)
...

解决问题的计划很简略,咱们将数据目录的权限设置为容器内的 ES 过程可操作即可:(强烈不举荐应用简略粗犷的 chmod 777

mkdir -p data
chown -R 1000:1000 data
docker-compose down && docker-compose up -d

执行完上述命令,重启容器过程之后,再次应用 docker-compose ps 命令查看应用状况,咱们能够看到程序运行失常。

# docker-compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
es-mastodon         "/tini -- /usr/local…"   es                  running (healthy)   9300/tcp

这部分的配置和代码也曾经上传至 GitHub,有须要能够自取:https://github.com/soulteary/Home-Network-Note/tree/master/example/mastodon/elasticsearch

利用搭建

根底服务搭建结束之后,咱们来实现利用的搭建和部署。

利用初始化

为了不便利用初始化,我写了一个简略的编排配置:

version: "3"
services:
  web:
    image: tootsuite/mastodon:v3.4.4
    restart: always
    environment:
      - "RAILS_ENV=production"
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; tail -f /etc/hosts"
    networks:
      - mastodon_networks

networks:
  mastodon_networks:
    external: true

将下面的内容保留为 docker-compose.init.yml,接着先应用 docker-compose up -d 启动一个 Mastodon 装置就绪的容器备用。

在容器启动之后,咱们执行上面的命令启动 Mastodon 装置疏导程序:

docker-compose -f docker-compose.init.yml exec web bundle exec rake mastodon:setup

执行结束下面的命令,会进入交互式命令行,咱们疏忽掉所有的正告信息,能够失去相似上面的日志(示例,你能够依据本人的状况调整)

Your instance is identified by its domain name. Changing it afterward will break things.
Domain name: hub.lab.com

Single user mode disables registrations and redirects the landing page to your public profile.
Do you want to enable single user mode? yes

Are you using Docker to run Mastodon? Yes

PostgreSQL host: db
PostgreSQL port: 5432
Name of PostgreSQL database: postgres
Name of PostgreSQL user: postgres
Password of PostgreSQL user: 
Database configuration works! 🎆

Redis host: redis
Redis port: 6379
Redis password: 
Redis configuration works! 🎆

Do you want to store uploaded files on the cloud? No

Do you want to send e-mails from localhost? yes
E-mail address to send e-mails "from": "(Mastodon <notifications@hub.lab.com>)"
Send a test e-mail with this configuration right now? no

This configuration will be written to .env.production
Save configuration? Yes
Below is your configuration, save it to an .env.production file outside Docker:

# Generated with mastodon:setup on 2022-01-24 08:49:51 UTC

# Some variables in this file will be interpreted differently whether you are
# using docker-compose or not.

LOCAL_DOMAIN=hub.lab.com
SINGLE_USER_MODE=true
SECRET_KEY_BASE=ce1111c9cd51305cd680aee4d9c2d6fe71e1ba003ea31cc27bd98792653535d72a13c386d8a7413c28d30d5561f7b18b0e56f0d0e8b107b694443390d4e9a888
OTP_SECRET=bcb50204394bdce54a0783f1ef2e72a998ad2f107a0ee4dc3b61557f5c12b5c76267c0512e3d08b85f668ec054d42cdbbe0a42ded70cbd0a70be70346e666d05
VAPID_PRIVATE_KEY=QzEMwqTatuKGLSI3x4gmFkFsxi2Vqd4taExqQtZMfNM=
VAPID_PUBLIC_KEY=BFBQg5vnT3AOW2TBi7OSSxkr28Zz2VZg7Jv203APIS5rPBOveXxCx34Okur-8Rti_sD07P4-rAgu3iBSsSrsqBE=
DB_HOST=db
DB_PORT=5432
DB_NAME=postgres
DB_USER=postgres
DB_PASS=mastodon
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
SMTP_SERVER=localhost
SMTP_PORT=25
SMTP_AUTH_METHOD=none
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_FROM_ADDRESS="Mastodon <notifications@hub.lab.com>"

It is also saved within this container so you can proceed with this wizard.

Now that configuration is saved, the database schema must be loaded.
If the database already exists, this will erase its contents.
Prepare the database now? Yes
Running `RAILS_ENV=production rails db:setup` ...


Database 'postgres' already exists
[strong_migrations] DANGER: No lock timeout set
Done!

All done! You can now power on the Mastodon server 🐘

Do you want to create an admin user straight away? Yes
Username: soulteary
E-mail: soulteary@gmail.com
You can login with the password: 76a17e7e1d52056fdd0fcada9080f474
You can change your password once you login.

在下面的交互程序中,为了节约工夫,我抉择了不应用内部服务存储文件、不应用内部服务发送邮件,你能够依据本人的需要进行调整。

在命令执行过程中,咱们可能会看到一些和 Redis 相干的报错信息:Error connecting to Redis on localhost:6379 (Errno::ECONNREFUSED)。这是因为咱们在启动配置程序,进行利用初始化的时候,并没有事后正确配置 Redis 服务器,这并不阐明咱们的配置是谬误的,只是尚未失效,不用惊恐。

这部分的配置和代码也曾经上传至 GitHub,有须要能够自取:https://github.com/soulteary/Home-Network-Note/tree/master/example/mastodon/app

更新利用配置

接下来,咱们须要将下面日志输入中和配置无关的信息保留到一个配置文件 .env.production 里。

# Generated with mastodon:setup on 2022-01-24 08:49:51 UTC

# Some variables in this file will be interpreted differently whether you are
# using docker-compose or not.

LOCAL_DOMAIN=hub.lab.com
SINGLE_USER_MODE=true
SECRET_KEY_BASE=ce1111c9cd51305cd680aee4d9c2d6fe71e1ba003ea31cc27bd98792653535d72a13c386d8a7413c28d30d5561f7b18b0e56f0d0e8b107b694443390d4e9a888
OTP_SECRET=bcb50204394bdce54a0783f1ef2e72a998ad2f107a0ee4dc3b61557f5c12b5c76267c0512e3d08b85f668ec054d42cdbbe0a42ded70cbd0a70be70346e666d05
VAPID_PRIVATE_KEY=QzEMwqTatuKGLSI3x4gmFkFsxi2Vqd4taExqQtZMfNM=
VAPID_PUBLIC_KEY=BFBQg5vnT3AOW2TBi7OSSxkr28Zz2VZg7Jv203APIS5rPBOveXxCx34Okur-8Rti_sD07P4-rAgu3iBSsSrsqBE=
DB_HOST=db
DB_PORT=5432
DB_NAME=postgres
DB_USER=postgres
DB_PASS=mastodon
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
SMTP_SERVER=localhost
SMTP_PORT=25
SMTP_AUTH_METHOD=none
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_FROM_ADDRESS="Mastodon <notifications@hub.lab.com>"

这里须要留神的一点是,发送邮件告诉配置中的 SMTP_FROM_ADDRESS 的内容须要应用双引号包裹,如果在下面交互式终端配置过程中,咱们应用回车“一路 Next”可能会呈现生成的配置内容漏加引号的问题。

如果呈现了这个问题,手动在保留文件的时候加上引号就行,不须要从新执行命令。

调整利用 Web 服务配置

和之前搭建基础设施和调整配置一样,咱们针对官网配置模版进行一个简略的调整,能够失去让服务运行最小的容器编排配置:

version: '3'
services:

  web:
    image: tootsuite/mastodon:v3.4.4
    restart: always
    env_file: .env.production
    environment:
      - "RAILS_ENV=production"
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
    networks:
      - mastodon_networks
    healthcheck:
     test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:3000/health || exit 1"]
     interval: 15s
     retries: 12
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro

  streaming:
    image: tootsuite/mastodon:v3.4.4
    env_file: .env.production
    restart: always
    command: node ./streaming
    networks:
      - mastodon_networks
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"]
      interval: 15s
      retries: 12
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      - "STREAMING_CLUSTER_NUM=1"
      - "NODE_ENV=production"

  sidekiq:
    image: tootsuite/mastodon:v3.4.4
    environment:
      - "RAILS_ENV=production"
    env_file: .env.production
    restart: always
    command: bundle exec sidekiq
    networks:
      - mastodon_networks
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


networks:
  mastodon_networks:
    external: true

将下面的内容保留后,咱们将服务启动。因为此时咱们并未映射任何端口到服务器“本地”,所以临时咱们还不能拜访这些服务。

为了解决这个问题,咱们须要配置 Mastodon 这个利用的前端代理。

配置服务前端代理

服务默认应用 Ruby Puma 作为 Web 服务器、Node Express 提供推送和实时更新。为了解决前端资源跨域问题、以及进一步晋升服务性能,咱们能够采纳 Nginx 对这些服务提供反向代理,将服务聚合在一起,并对其中的动态资源进行肯定的缓存。

官网这里有一个默认的模版,https://github.com/mastodon/mastodon/blob/main/dist/nginx.conf,不过这个配置实用于不应用容器、或者利用都运行在容器,Nginx 不应用容器运行的场景。

思考到咱们应用 Traefik 提供动静的服务注册和 SSL 证书挂载,所以这个配置咱们须要稍作调整能力应用(仅展现次要改变)。

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://web:3000;
  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;
}

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://streaming: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;
}

在下面的配置中,Nginx“整合”了咱们提到的来自 Ruby 和 Node 的两套 Web 服务,并且针对动态资源做了根底的缓存操作,对于能够缓存的内容做了长达一周的 LRU 缓存。

看到这里,咱们的服务仿佛能失常的跑起来了。然而,是真的没有问题吗?

利用问题修改和架构调优

当咱们将服务运行起来之后,即便利用看上去一切正常,此刻咱们会遇到第一个问题。日志里频繁呈现“X-Accel-Mapping header missing”的正告提醒。

触发这个问题的起因在 https://github.com/mastodon/mastodon/issues/3221 中有被披露,不过社区并没有给出好的解决方案。解决这个问题其实很简略,将动态资源彻底从 Ruby Web 服务中迁出即可:一来能够解决这个问题,二来则能够晋升服务整体性能,以及在将来让服务更容易做程度扩大。

同时,当咱们尝试上传图片或者视频的时候,你会发现因为容器挂载目录的权限问题,咱们始终会失去谬误的返回。有的人会应用 chmod 777 大法解决问题,然而这个并不是一个最佳实际:存在潜在的平安问题,并且让你的利用程度扩大的能力变得很差。

当然,还有一些细节问题,咱们稍后再解决,先解决以上两个问题。

拆分动态资源服务

提到利用动静资源拆分,在云服务大环境下咱们未免会想到 CDN。在 Mastodon 中,利用反对设置 CDN_HOST 来将动态资源拆分到 CDN 服务器。不过少数的服务维护者会采纳让 CDN 动静回源的计划来进行实现,在疏忽肯定水平的数据一致性的前提下,这样的保护老本非常低,无需做任何调整和利用改变。

然而仅仅这样做并解决不了咱们在前文中提到的问题(CDN 时效到了,还是会回源出触发下面的问题)。并且也不利于私有化部署和应用(有额定的老本,不得不依赖公网服务)。

这里有一个更好的计划是 将咱们的动态资源从新封装为一个独立的服务运行

参考以往文章中针对容器进行多阶段构建和优化的内容,很容易能够写出相似上面的 Dockerfile:

FROM tootsuite/mastodon:v3.4.4 AS Builder

FROM nginx:1.21.4-alpine
COPY --from=Builder /opt/mastodon/public /usr/share/nginx/html

应用 docker build -t mastodon-assets . 将 Mastodon 的动态资源和 Nginx 打包为一个新的镜像之后,接着来编写这个服务的容器编排配置:

version: '3'
services:

  mastodon-assets:
    image: mastodon-assets
    restart: always
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"

      - "traefik.http.middlewares.cors.headers.accessControlAllowMethods=GET,OPTIONS"
      - "traefik.http.middlewares.cors.headers.accessControlAllowHeaders=*"      
      - "traefik.http.middlewares.cors.headers.accessControlAllowOriginList=*"
      - "traefik.http.middlewares.cors.headers.accesscontrolmaxage=100"
      - "traefik.http.middlewares.cors.headers.addvaryheader=true"

      - "traefik.http.routers.mastodon-assets-http.middlewares=cors@docker"
      - "traefik.http.routers.mastodon-assets-http.entrypoints=http"
      - "traefik.http.routers.mastodon-assets-http.rule=Host(`hub-assets.lab.com`)"
 
      - "traefik.http.routers.mastodon-assets-https.middlewares=cors@docker"
      - "traefik.http.routers.mastodon-assets-https.entrypoints=https"
      - "traefik.http.routers.mastodon-assets-https.tls=true"
      - "traefik.http.routers.mastodon-assets-https.rule=Host(`hub-assets.lab.com`)"

      - "traefik.http.services.mastodon-assets-backend.loadbalancer.server.scheme=http"
      - "traefik.http.services.mastodon-assets-backend.loadbalancer.server.port=80"

networks:
  traefik:
    external: true

将下面的内容保留为 docker-compose.yml 之后,应用 docker-compose up -d 启动服务,就将本来应用 Ruby 服务吞吐的动态资源,切换到了应用独立的 Nginx 服务来实现动态资源吞吐的目标了。

当然,为了这个操作可能失效,咱们还须要在 .env.production 中增加上面的配置内容:

CDN_HOST=https://hub-assets.lab.com

独立保护上传资源

前文提到过,在默认的容器利用中,程序逻辑是让 Ruby 利用保护和解决咱们上传的媒体文件(图片、视频)。这个计划同样不利于服务将来的程度扩大和拆分到适合的机器上运行,一个绝对更好的计划是应用 S3 服务来针对用户上传的文件进行治理,让利用靠近于无状态运行。

在《装在笔记本里的公有云环境:网络存储篇(上)》和《装在笔记本里的公有云环境:网络存储篇(中)》两篇内容中,我有介绍过如何应用 MinIO 来作为通用的存储网关应用。所以,如何搭建和监控一个公有的 S3 服务,在这里就不再赘述了,这里仅聊聊一些不同之处。

这里我采纳的是同机部署,所以服务之间的拜访,是通过虚构网卡来解决的。因为服务都在 Traefik“前面”,所以交互协定也尽能够脱去 HTTPS(让 Mastodon 间接应用容器服务名称拜访即可)。

这里有一个小细节,为了服务的失常运行,咱们的 S3 Entrypoint 须要应用常见端口,比方 HTTP(80)、HTTPS(443),所以在 MinIO 服务中的运行命令须要调整为:

command: minio server /data --address 0.0.0.0:80 --listeners 1  --console-address 0.0.0.0:9001

但如果咱们应用 HTTP 的话,会引出另外一个问题,就是 Mastodon 展现动态资源的时候,会应用 HTTP 协定而不是咱们冀望的 HTTPS,这会造成在 Web 界面的媒体资源无奈展现的问题。(不影响客户端,如何解决限于篇幅,咱们将在下篇内容中提到)

此外在 Mastodon 中应用 S3 服务作为文件存储后端,因为 S3 服务默认提供的 URL 门路是 S3_DOMAIN_NAME/S3_BUCKET_NAME,所以咱们须要在 S3_ALIAS_HOST 配置中做雷同的设置。不过思考到资源的拜访性能和效率问题,咱们同样能够启动一个 Nginx 作为 MinIO 的动态资源缓存,并且进一步简化这个配置,让咱们间接设置 S3_DOMAIN_NAME 即可,同样会不便咱们后续进行程序定制。

先来编写这个服务的编排配置:

version: "3"
services:

  nginx-minio:
    image: nginx:1.21.4-alpine
    restart: always
    networks:
      - traefik
      - mastodon_networks
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 15s
      retries: 12
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.mastodon-s3-http.entrypoints=http"
      - "traefik.http.routers.mastodon-s3-http.rule=Host(`hub-res.lab.com`)"
      - "traefik.http.routers.mastodon-s3-https.entrypoints=https"
      - "traefik.http.routers.mastodon-s3-https.tls=true"
      - "traefik.http.routers.mastodon-s3-https.rule=Host(`hub-res.lab.com`)"
      - "traefik.http.services.mastodon-s3-backend.loadbalancer.server.scheme=http"
      - "traefik.http.services.mastodon-s3-backend.loadbalancer.server.port=80"

  minio:
    image: ${DOCKER_MINIO_IMAGE_NAME}
    container_name: ${DOCKER_MINIO_HOSTNAME}
    volumes:
      - ./data/minio/data:/data:z
    command: minio server /data --address 0.0.0.0:80 --listeners 1  --console-address 0.0.0.0:9001
    environment:
      - MINIO_ROOT_USER=${MINIO_ROOT_USER}
      - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
      - MINIO_REGION_NAME=${MINIO_REGION_NAME}
      - MINIO_BROWSER=${MINIO_BROWSER}
      - MINIO_BROWSER_REDIRECT_URL=${MINIO_BROWSER_REDIRECT_URL}
      - MINIO_PROMETHEUS_AUTH_TYPE=public
    restart: always
    networks:
      - traefik
      - mastodon_networks
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.middlewares.minio-gzip.compress=true"
      - "traefik.http.routers.minio-admin.middlewares=minio-gzip"
      - "traefik.http.routers.minio-admin.entrypoints=https"
      - "traefik.http.routers.minio-admin.tls=true"
      - "traefik.http.routers.minio-admin.rule=Host(`${DOCKER_MINIO_ADMIN_DOMAIN}`)"
      - "traefik.http.routers.minio-admin.service=minio-admin-backend"
      - "traefik.http.services.minio-admin-backend.loadbalancer.server.scheme=http"
      - "traefik.http.services.minio-admin-backend.loadbalancer.server.port=9001"
    extra_hosts:
      - "${DOCKER_MINIO_HOSTNAME}:0.0.0.0"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/minio/health/live"]
      interval: 3s
      retries: 12
    logging:
      driver: "json-file"
      options:
        max-size: "10m"

networks:
  mastodon_networks:
    external: true
  traefik:
    external: true

接着来进行 Nginx 的配置编写:

server {
    listen 80;
    server_name localhost;

    keepalive_timeout 70;
    sendfile on;
    client_max_body_size 80m;

    location / {
        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 Host $http_host;

        proxy_connect_timeout 300;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        chunked_transfer_encoding off;

        proxy_pass http://minio/mastodon/;
    }

    location /health {
        access_log off;
        return 200 "ok";
    }
}

而后是 MinIO 初始化脚本 docker-compose.init.yml 的编写:

version: "3"
services:

  minio-client:
    image: ${DOCKER_MINIO_CLIENT_IMAGE_NAME}
    entrypoint: >
      /bin/sh -c "
      /usr/bin/mc config host rm local;
      /usr/bin/mc config host add --quiet --api s3v4 local http://minio ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
      /usr/bin/mc mb --quiet local/${DEFAULT_S3_UPLOAD_BUCKET_NAME}/;
      /usr/bin/mc policy set public local/${DEFAULT_S3_UPLOAD_BUCKET_NAME};
      "
    networks:
      - traefik

networks:
  traefik:
    external: true

最初是 MinIO 运行所须要的根底配置信息 .env

# == MinIO
# optional: Set a publicly accessible domain name to manage the content stored in Outline

DOCKER_MINIO_IMAGE_NAME=minio/minio:RELEASE.2022-01-08T03-11-54Z
DOCKER_MINIO_HOSTNAME=mastodon-s3-api.lab.com
DOCKER_MINIO_ADMIN_DOMAIN=mastodon-s3.lab.com
MINIO_BROWSER=on
MINIO_BROWSER_REDIRECT_URL=https://${DOCKER_MINIO_ADMIN_DOMAIN}
# Select `Lowercase a-z and numbers` and 16-bit string length https://onlinerandomtools.com/generate-random-string
MINIO_ROOT_USER=6m2lx2ffmbr9ikod
# Select `Lowercase a-z and numbers` and 64-bit string length https://onlinerandomtools.com/generate-random-string
MINIO_ROOT_PASSWORD=2k78fpraq7rs5xlrti5p6cvb767a691h3jqi47ihbu75cx23twkzpok86sf1aw1e
MINIO_REGION_NAME=cn-homelab-1

# == MinIO Client
DOCKER_MINIO_CLIENT_IMAGE_NAME=minio/mc:RELEASE.2022-01-07T06-01-38Z

DEFAULT_S3_UPLOAD_BUCKET_NAME=mastodon

如何启动和应用 MinIO 在之前的文章中有介绍,限于篇幅字数限度,就不做开展了。感兴趣请自行翻阅,相干的代码曾经上传至 GitHub https://github.com/soulteary/Home-Network-Note/tree/master/example/mastodon/minio

最终利用配置

好了,将下面的内容稍加整合,再做一些简略的调整,咱们便能够失去相似上面的配置啦:

version: '3'
services:

  mastodon-gateway:
    image: nginx:1.21.4-alpine
    restart: always
    networks:
      - traefik
      - mastodon_networks
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./nginx.conf:/etc/nginx/nginx.conf
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 15s
      retries: 12
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.mastodon-nginx-http.entrypoints=http"
      - "traefik.http.routers.mastodon-nginx-http.rule=Host(`hub.lab.com`)"
      - "traefik.http.routers.mastodon-nginx-https.entrypoints=https"
      - "traefik.http.routers.mastodon-nginx-https.tls=true"
      - "traefik.http.routers.mastodon-nginx-https.rule=Host(`hub.lab.com`)"
      - "traefik.http.services.mastodon-nginx-backend.loadbalancer.server.scheme=http"
      - "traefik.http.services.mastodon-nginx-backend.loadbalancer.server.port=80"

  web:
    image: tootsuite/mastodon:v3.4.4
    restart: always
    env_file: .env.production
    environment:
      - "RAILS_ENV=production"
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
    networks:
      - mastodon_networks
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:3000/health || exit 1"]
      interval: 15s
      retries: 12
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro

  streaming:
    image: tootsuite/mastodon:v3.4.4
    env_file: .env.production
    restart: always
    command: node ./streaming
    networks:
      - mastodon_networks
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"]
      interval: 15s
      retries: 12
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      - "STREAMING_CLUSTER_NUM=1"

  sidekiq:
    image: tootsuite/mastodon:v3.4.4
    env_file: .env.production
    restart: always
    command: bundle exec sidekiq
    networks:
      - mastodon_networks
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro

networks:
  mastodon_networks:
    external: true
  traefik:
    external: true

此外,因为咱们配置了独立的动态资源服务和文件存储服务,所以须要在 .env.production 中额定增加一些配置:

CDN_HOST=https://hub-assets.lab.com
S3_ENABLED=true
S3_PROTOCOL=http
S3_REGION=cn-homelab-1
S3_ENDPOINT=http://mastodon-s3-api.lab.com
S3_BUCKET=mastodon
AWS_ACCESS_KEY_ID=6m2lx2ffmbr9ikod
AWS_SECRET_ACCESS_KEY=2k78fpraq7rs5xlrti5p6cvb767a691h3jqi47ihbu75cx23twkzpok86sf1aw1e
S3_ALIAS_HOST=hub-res.lab.com

应用相熟的 docker-compose up -d 启动服务,稍等片刻,咱们便能看到失常启动的利用了。

这部分相干的代码,曾经上传至 GitHub https://github.com/soulteary/Home-Network-Note/tree/master/example/mastodon/app,有须要自取即可。

点击登录,应用咱们刚刚创立利用配置时的账号邮箱和初始化明码即可实现利用登陆,开始对 Mastodon 的摸索啦。

最初

即便一再精简内容,本文的字数也超过了少数平台的长度限度,所以如果你在浏览的过程中发现有一部分缺失,能够尝试浏览原文或者 GitHub 上的残缺示例文件来解决问题。

下一篇文章中,我将聊聊如何针对性能进一步做一些调优操作,以及解决本文未解决完的一些问题。

后续将陆续整顿和分享一些在常识治理、知识库建设过程中的小教训,心愿能帮忙到同样对这个畛域感兴趣、充斥好奇心的你。

–EOF


如果你感觉内容还算实用,欢送点赞分享给你的敌人,在此谢过。

如果你想更快的看到后续内容的更新,请不吝“点赞”或“转发分享”,这些收费的激励将会影响后续无关内容的更新速度。


本文应用「署名 4.0 国内 (CC BY 4.0)」许可协定,欢送转载、或从新批改应用,但须要注明起源。署名 4.0 国内 (CC BY 4.0)

本文作者: 苏洋

原文链接: https://soulteary.com/2022/01…

正文完
 0