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

60次阅读

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

本篇文章是应用 Mastodon 搭建个人信息平台的第二篇内容,我将聊聊在容器环境中搭建 Mastodon 后的一些利用调整和问题修复。

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

写在后面

本篇内容须要有上一篇内容的根底,所以如果你还未浏览上一篇内容,能够思考移步过来,浏览理解《应用 Mastodon 搭建个人信息平台:前篇》。

在上篇文章完结后,咱们曾经能够通过手机利用进行登录和发帖记录信息了,然而在 Web 端应用的话,还是会遇到一些影响体验的小问题,同时,利用运行时应用的资源也会绝对节约,所以本篇内容就来解决这些问题。

为了关照新人,解决问题的程序依照从简到难,先从根底的服务配置开始吧。

如何启用 ES 全文搜寻

在登录账号之后,在侧边栏抉择“首选项”,关上利用后盾页面。在后盾页面的侧边栏中抉择“治理”,就能够看到展现利用以后运行状况的信息面板啦。

在图片中咱们能够看到“服务器配置”中的“全文搜寻”目前是敞开着的。

为了让服务失常应用,咱们须要在前文中提到的配置文件 .env.production 中增加一些内容:

ES_ENABLED=true
ES_HOST=es
ES_PORT=9200

接着应用 docker-compose down && docker-compose up -d 重启服务,稍等服务运行就绪之后,咱们就可能看到“全文搜寻”曾经启用啦。

加载字体资源报错的问题

在利用控制台中,咱们会看到一条扎眼的报错。

Refused to load the font 'data:application/font-woff2;base64,...' because it violates the following Content Security Policy directive: "font-src'self'https://hub-assets.lab.com".

这是因为 config/initializers/content\_security\_policy.rb 中的设置比拟严格导致:

Rails.application.config.content_security_policy do |p|
  p.base_uri        :none
  p.default_src     :none
  p.frame_ancestors :none
  p.font_src        :self, assets_host
  p.img_src         :self, :https, :data, :blob, assets_host
  p.style_src       :self, assets_host
  p.media_src       :self, :https, :data, assets_host
  p.frame_src       :self, :https
  p.manifest_src    :self, assets_host

  if Rails.env.development?
...
  else
    p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url
    p.script_src  :self, assets_host
    p.child_src   :self, :blob, assets_host
    p.worker_src  :self, :blob, assets_host
  end
end

解决这个问题很简略,只须要在 font 资源的安全策略中容许 data: 类型的资源即可:

...
  p.font_src        :self, :data, assets_host
...

因为咱们应用的是容器中的 Mastodon,为了保障“打补丁”的程序和运行中的统一,能够从运行容器中将所须要的文件复制到本地。

docker cp app-web-1:/opt/mastodon/config/initializers/content_security_policy.rb .

在调整之后,能够应用文件挂载的形式将文件映射回容器。

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
    - ./content_security_policy.rb:/opt/mastodon/config/initializers/content_security_policy.rb:ro

接着应用 docker-compose down && docker-compose up -d 重启服务,稍等服务运行就绪之后,咱们就可能看到这条谬误曾经隐没啦。

解决页面中图片不展现的问题

尽管咱们在上篇文章中,将 Mastodon 应用的资源文件都应用 MinIO 进行了存储,在上传过程中也可能正确的进行文件上传和存储。

然而在应用的过程中,不出意外,会遇到上面两个正告和谬误提醒,而导致页面无奈显示图片。

Mixed Content: The page at 'https://hub.lab.com/web/timelines/home' was loaded over HTTPS, but requested an insecure element 'http://hub-res.lab.com/accounts/avatars/107/676/580/770/436/146/original/c80b9f6855890db4.png'. This request was automatically upgraded to HTTPS, For more information see https://blog.chromium.org/2019/10/no-more-mixed-messages-about-https.html

Refused to load the image 'http://hub-res.lab.com/media_attachments/files/107/677/586/354/795/556/original/08697ec4a7c4a362.jpeg' because it violates the following Content Security Policy directive: "img-src'self'https: data: blob: https://hub-assets.lab.com".

其中一个问题的解决方案和上文中解决“加载字体资源报错”雷同,须要调整 content_security_policy.rb 中的规定来解决问题。

p.img_src         :self, :https, :data, :blob, assets_host

咱们在下面的规定中增加动态资源服务器地址即可:

p.img_src         :self, :https, :data, :blob, assets_host, 'http://hub-res.lab.com', 'https://hub-res.lab.com'

在和上文应用雷同操作,将批改后的文件映射回容器后,重启利用,就能够看到浏览器回绝加载资源的谬误曾经隐没了。然而混合资源加载的正告则降级成为了谬误。

所以接下来咱们要解决一个新的问题:加载资源并未应用 HTTPS。

解决 S3 资源未应用 HTTPS 的问题

页面资源之所以会应用 HTTP 形式加载,次要的起因是 Mastodon 应用的一个依赖库 https://github.com/thoughtbot/paperclip,在解决资源上传和资源展现的逻辑上解决的比拟死板。如果你在上传资源的时候应用的是 HTTP 协定,那么在申请资源的时候,也会默认应用雷同的协定。

而在上一篇文章里,咱们有提到在同机部署的情况下,在雷同容器网络中,能够间接应用 HTTP 进行服务间调用(省略掉为容器和零碎装置自签名证书的麻烦)。

个别状况下,我会思考间接对这类间接产生作用的库进行调整,然而这个存在了十年之久的库,曾经被作者宣告废除:“Paperclip is deprecated”。并且举荐咱们进行工具迁徙,或者在接下来的版本中,Mastodon 或者会因而进行局部性能的调整或者重构。所以在解决这个问题的时候,咱们有两个抉择,一个是将补丁打在利用自身,另外一个则是把补丁打在 PaperClip 上。

将补丁打在依赖库上

先来聊聊副作用最小的形式,将补丁打在依赖库上,仅在输入 S3 资源的时候调整资源应用的协定。

通过简略的调用追踪,能够看到负责输入 S3 动态资源的逻辑在 https://github.com/thoughtbot/paperclip/blob/main/lib/paperclip/storage/s3.rb 这个文件中。

Paperclip.interpolates(:s3_alias_url) do |attachment, style|
  protocol = attachment.s3_protocol(style, true)
  host = attachment.s3_host_alias
  path = attachment.path(style).
    split("/")[attachment.s3_prefixes_in_alias..-1].
    join("/").
    sub(%r{\A/}, "".freeze)"#{protocol}//#{host}/#{path}"
end unless Paperclip::Interpolations.respond_to? :s3_alias_url

解决的形式很简略,只需将 protocol 调整为咱们所须要的值即可(应用 ENV、参数传递、或者 HardCode 都能够),比方:

protocol = "https:"

和上文一样,应用命令将文件拷贝进去:

docker cp app-web-1:/opt/mastodon/vendor/bundle/ruby/2.7.0/gems/paperclip-6.0.0/lib/paperclip/storage/s3.rb .

在批改结束之后,将文件挂载回容器,再重启容器,你会发现问题就解决啦。

将补丁打在应用程序上

咱们也能够将补丁打在利用自身,一劳永逸的解决问题,不过相比拟前者,在性能上会有一丢丢的损失。

通过简略的调用追踪,咱们能够看到在页面中输入媒体资源的逻辑在 app/serializers/rest/media_attachment_serializer.rb 这个文件中:

def url
  if object.not_processed?
    nil
  elsif object.needs_redownload?
    media_proxy_url(object.id, :original)
  else
    full_asset_url(object.file.url(:original))
  end
end

def remote_url
  object.remote_url.presence
end

def preview_url
  if object.needs_redownload?
    media_proxy_url(object.id, :small)
  elsif object.thumbnail.present?
    full_asset_url(object.thumbnail.url(:original))
  elsif object.file.styles.key?(:small)
    full_asset_url(object.file.url(:small))
  end
end

其中 full_asset_url 这个函数就是咱们要尝试打补丁的“家伙”。在 https://github.com/mastodon/mastodon/blob/main/app/helpers/routing\_helper.rb 能够找到这个函数的真身:

def full_asset_url(source, **options)
  source = ActionController::Base.helpers.asset_url(source, **options) unless use_storage?

  URI.join(root_url, source).to_s
end

...

private

def use_storage?
  Rails.configuration.x.use_s3 || Rails.configuration.x.use_swift
end

同样的,咱们应用命令将容器中的程序文件拷贝到本地:

docker cp app-web-1:/opt/mastodon/app/helpers/routing_helper.rb .

因为咱们只须要在解决 S3 相干资源的时候打补丁,所以能够应用上面的形式对程序进行调整:

def full_asset_url(source, **options)
  source = ActionController::Base.helpers.asset_url(source, **options).gsub("http://", "https://") unless use_storage?
  URI.join(root_url, source).to_s
end

在批改结束文件之后,将文件挂载回容器中,接着重启容器,问题也就解决了。

解决前端资源应用谬误协定

不管你应用下面哪一种计划,在问题解决后,你会发现哪怕页面 meta 信息、接口响应字段中都是 https 协定的主机地址,Mastodon Web 端在渲染界面中图片的时,始终会触发两次元素绘制,第一次明明还是正确的后果,到了第二次就变成了内容一样,然而资源地址以 http 的后果了 …

坦白说 Mastodon 前端实现比拟乱(主线版本和稳固版本目录构造差别也比拟大),治理形式也比拟奇怪(相似 Flarum,用次要技术栈来治理前端资源和构建),我就不做深刻的动静调试了。

所以我抉择间接在输入渲染的中央进行全局协定替换,毕竟咱们的 Mastodon 是运行在 HTTPS 协定下,并开启了严格 CSP 规定。这样的场景下是不可能再引入 HTTP 的页面资源的。

简略定位,能够看到页面中输入资源的逻辑在 https://github.com/mastodon/mastodon/blob/main/app/javascript/mastodon/components/media\_gallery.js 中进行解决:

const previewUrl   = attachment.get('preview_url');
const previewWidth = attachment.getIn(['meta', 'small', 'width']);

const originalUrl   = attachment.get('url');
const originalWidth = attachment.getIn(['meta', 'original', 'width']);

const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number';

const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
const sizes  = hasSize && (displayWidth > 0) ? `${displayWidth * (width / 100)}px` : null;

const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
const x      = ((focusX /  2) + .5) * 100;
const y      = ((focusY / -2) + .5) * 100;

thumbnail = (
  <a
    className='media-gallery__item-thumbnail'
    href={attachment.get('remote_url') || originalUrl}
    onClick={this.handleClick}
    target='_blank'
    rel='noopener noreferrer'
  >

通过动静调试,能够看到页面的资源是什么,是否正确展现(有正确的协定头)其实少数场景下都是由 originalUrl 这个变量来决定的,所以咱们针对它做一个字符串替换就行了。当然,为了保险,能够将另外一个有相似性能,然而常常数值为空的变量 previewUrl 也做雷同解决:

const previewUrl   = attachment.get('preview_url').replace(/^http:/,'https:');
const previewWidth = attachment.getIn(['meta', 'small', 'width']);

const originalUrl   = attachment.get('url').replace(/^http:/,'https:');
const originalWidth = attachment.getIn(['meta', 'original', 'width']);

...

和下面不同的是,咱们除了须要将源文件拷贝进去进行批改之外,还须要将代码进行从新构建,才可能应用。

docker cp app-web-1:/opt/mastodon/app/javascript/mastodon/components/media_gallery.js .

参考前文中剥离 Mastodon 动态资源和主利用的容器,将打补丁后的程序进行从新编译,而后更新资源镜像:

FROM tootsuite/mastodon:v3.4.4 AS Builder
ENV RAILS_ENV="production"
ENV NODE_ENV="production"
COPY media_gallery.js /opt/mastodon/app/javascript/mastodon/components/media_gallery.js
RUN cd ~ && \
    OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile && \
    yarn cache clean

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

期待镜像构建结束,重启服务,并彻底清除页面缓存(尤其是 Worker)后,再次尝试发一个带有图片的内容,你会发现所有都失常啦。

去除 FloC 隐衷沙盒正告

在利用的 Web 控制台中,咱们可能看到一条乏味的谬误提醒。

Error with Permissions-Policy header: Unrecognized feature: 'interest-cohort'.

Mastodon 默认会在 config/environments/production.rb 文件中申明 Permissions-Policy 响应头的内容为 interest-cohort=(),来禁止浏览器对咱们进行追踪和剖析。

  config.action_dispatch.default_headers = {
    'Server'                 => 'Mastodon',
    'X-Frame-Options'        => 'DENY',
    'X-Content-Type-Options' => 'nosniff',
    'X-XSS-Protection'       => '0',
    'Permissions-Policy'     => 'interest-cohort=()',}

下面这条正告的起源是 FloC 性能尚未启用,浏览器无奈依据服务端输入的响应头 Permissions-Policy 来执行对应的操作。

在 Chrome 浏览器中关上 chrome://settings/privacySandbox,能够看到以后用户是否关上或敞开了 FloC 性能。对于 FloC 的更多材料,能够从 https://web.dev/floc/ 理解。

如果想革除掉这条正告,只须要批改下面提到的文件,将该响应字段删除即可。

缩小利用资源占用

因为我的目标是集体应用,所以我冀望这套服务能够尽可能的“绿色环保”。尽量少应用一些资源,为其余利用留一些 Buffer。

缩小 Streaming 服务资源使用量

影响 Streaming 服务的资源使用量次要因素有两个因素:是否开启了生产模式、是否限度了 Worker 的数量。前者不光是印象 Streaming 的行为,同时会影响它引入的各种内部框架和软件包的行为;后者则默认会依据你运行环境的 CPU 数量来做一个资源分配,对于个人用户而言,有一个 Worker 就足够了。

通过浏览代码,咱们能够看到,管制这两个因素的变量和具体代码实现:

...
const env = process.env.NODE_ENV || 'development';

...
const numWorkers = +process.env.STREAMING_CLUSTER_NUM || (env === 'development' ? 1 : Math.max(os.cpus().length - 1, 1));

理解了是哪个变量管制服务,那么将变量配置到容器编排文件中即可:

  streaming:
    ...
    environment:
      - "NODE_ENV=production"
      - "STREAMING_CLUSTER_NUM=1"

重启利用,咱们可能看到日志输入中就只会有一个 Worker 了。

WARN Starting streaming API server master with 1 workers 
WARN Starting worker 1 
WARN Worker 1 now listening on 0.0.0.0:4000 
verb Subscribe timeline:access_token:4 
verb 01214813-cadf-4e28-855a-c96ccca243c6 Starting stream from timeline:107676580770436146 for 107676580770436146
verb Subscribe timeline:107676580770436146 

缩小 Web 利用资源占用

Mastodon 应用的 Web 服务是 Puma,默认启动后,查看日志咱们能够看到过程的应用状况:

[9] Puma starting in cluster mode...
[9] * Puma version: 5.3.2 (ruby 2.7.2-p137) ("Sweetnighter")
[9] *  Min threads: 5
[9] *  Max threads: 5
[9] *  Environment: production
[9] *   Master PID: 9
[9] *      Workers: 2
[9] *     Restarts: (✔) hot (✖) phased
[9] * Preloading application
[9] * Listening on http://0.0.0.0:3000
[9] Use Ctrl-C to stop
[9] - Worker 0 (PID: 20) booted in 0.02s, phase: 0
[9] - Worker 1 (PID: 24) booted in 0.01s, phase: 0

这里对于集体应用而言,比拟节约,咱们也做一个数值下调。并且因为咱们曾经用 Nginx 剥离了动态资源,所以还能够设置不应用 Puma 来提供动态资源服务。

  web:
...
    environment:
      - "RAILS_ENV=production"
      - "MAX_THREADS=2"
      - "WEB_CONCURRENCY=1"
      - "RAILS_SERVE_STATIC_FILES=false"
...

重启服务,能够看到配置曾经失效了。

[9] Puma starting in cluster mode...
[9] * Puma version: 5.3.2 (ruby 2.7.2-p137) ("Sweetnighter")
[9] *  Min threads: 2
[9] *  Max threads: 2
[9] *  Environment: production
[9] *   Master PID: 9
[9] *      Workers: 1
[9] *     Restarts: (✔) hot (✖) phased
[9] * Preloading application
[9] * Listening on http://0.0.0.0:3000
[9] Use Ctrl-C to stop
[9] ! WARNING: Detected running cluster mode with 1 worker.
[9] ! Running Puma in cluster mode with a single worker is often a misconfiguration.
[9] ! Consider running Puma in single-mode (workers = 0) in order to reduce memory overhead.
[9] ! Set the `silence_single_worker_warning` option to silence this warning message.
[9] - Worker 0 (PID: 20) booted in 0.0s, phase: 0

让 Sidekiq 运行的更有安全感

Sidekiq 负责解决所有的异步工作和打算工作,对于这类组件,个别倡议是在资源冗余的状况下,尽快的让工作计算结束,防止沉积,最终造成服务雪崩。

所以并不倡议对其进行设置,将工作并发解决量减少。如果你切实介意默认的并发数量,能够在 mastodon/config/sidekiq.yml 配置文件中调整数值到你冀望的水平(默认资源占用其实也不高)。

不过 Mastodon 官网也好,社区也罢,并没有针对 Mastodon 做服务运行状况查看,所以这里咱们针对 Sidekiq 做一个简略的健康检查,保障服务可能在极其状况下主动复原即可。

  sidekiq:
...
    healthcheck:
      test: ["CMD-SHELL", "ps aux | grep'[s]idekiq\ 6'|| false"]
      interval: 15s
      retries: 12

至于过程匹配命令为何会这样写呢,感兴趣的同学能够浏览 How does this [t]ricky bracket expression in grep work?,来理解一个冷常识。

利用资源应用概览

一通操作下来,在应用一阵 Mastodon 后,咱们能够看到各个容器对资源的具体应用状况,除了两个 Ruby 小户比拟吃资源外,能够看到其余的利用的内存耗费都在 100MB(少数远远低于这个数值),CPU 用量更是低到能够忽略不计,基本上到了能够承受的范畴内。

NAME                                CPU %     MEM USAGE / LIMIT     MEM %
mastodon-api                        0.02%     308.8MiB / 7.369GiB   4.09%
nginx-mastodon                      0.00%     3.449MiB / 7.369GiB   0.05%
streaming                           0.01%     30.85MiB / 7.369GiB   0.41%
sidekiq                             0.43%     259.7MiB / 7.369GiB   3.44%
nginx-assets                        0.00%     3.898MiB / 7.369GiB   0.05%
redis                               0.47%     4.137MiB / 7.369GiB   0.05%
postgres                            0.00%     38.64MiB / 7.369GiB   0.51%
nginx-minio                         0.00%     7.289MiB / 7.369GiB   0.10%
minio                               0.00%     79.41MiB / 7.369GiB   1.05%

对于 ES 的抉择和资源应用问题,在上篇文章中曾经提到了,故不反复开展形容。

其余

如果你心愿更深刻的调整和优化这个 Ruby 我的项目,能够参考我之前的一篇文章进行操作:《Ruby 利用容器封装踩坑记录(Lobsters)》。

最初

写到这里,本篇文章的目标就达到了。

本文中相干的代码,能够在 GitHub 上的开源仓库中找到,也欢送提供更好的计划。

下一篇文章中,我将聊聊如何疾速开发和集成机器人,让作为个人信息平台的 Mastodon 的信息流变的更有价值,交互方式更乏味。后续也将陆续整顿和分享一些在常识治理、知识库建设过程中的小教训,心愿能帮忙到同样对这个畛域感兴趣、充斥好奇心的你。

–EOF


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

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


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

本文作者: 苏洋

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

正文完
 0