Linux 利用 systemd 在 Ubuntu 執行 Sidekiq 6

kevinluo201 · 2021年08月21日 · 662 次阅读

网志版:https://dev.to/kevinluo201/systemd-ubuntu-sidekiq-6-d9c

自从有了 Sidekiq 后,所有事情都变得容易了。 所有需要长时间执行的程式,我全往 sidekiq 扔,我再也不用怕 CloudFlare 的 524(请求超时) 了 (咦?) 在 production 的环境执行 sidekiq 虽不困难但对于 Linux 没那麽熟的人来说还满多细节要注意的, Sidekiq 6 又好像跟之前变化颇大。 这次想分享一下将 Sidekiq 6 运行在 ubuntu 20.04 的经验 : )

目标

  • 运行 Sidekiq (废话...)
  • 重开机或 sidekiq 挂了的话,sidekiq 会自动重启
  • 当我用 Capistrano 部署新的程式码时,sidekiq 会读新的程式码并重启

我们怎麽执行 Sidekiq?

要知道在 production 环境怎麽运行 sidekiq, 首先要知道在 development 环境是怎麽跑的:

bundle exec sidekiq

其实就那麽容易... 你可以再加一些参数像 -C xxxx.yml 去指定要读取的设定档。不过我们先维持这样

基本上你在 production 环境可以做一样的事,如果你想让它更像一个”真的服务”,也可以直接加个“&”在后面让它跑在背景:

RAILS_ENV=production bundle exec sidekiq &

如果真的纯粹只是想要在 production 环境跑 sidekiq,其实这样就够了

但光这样做好像没符合我们的目标:

  1. 重开机或程式挂掉后 sidekiq 不会自动运行
  2. 部署完后也不会自动运行
  3. 最重要的是,这感觉好像不是很”Pro”。即使我们不是用 Sidekiq Pro , 应该也可以 Pro 一点

好,为了达到我们的目标,我们需要用 systemd

systemd 是什麽?

systemd 是 Linux 系统专门来管理各式「服务程式」的程式,其实就是 daemon 所以才是 system*d*。比如 mysql, apache, nginx, redis 这些都可以用它来管,事实上 systemd 是多数 Linux 版本预设的 Service Manager。 有 systemd,,我们可以:

  • systemctl start/stop/restart 任何服务
  • 可以启用 (enable) 服务,启用的服务在系统重开时会自动开始运行
  • 你可以指定当程式挂掉后,该做什麽事,例如重启该服务

systemd 的内容还有一堆,不过目前知道这样就足够了,剩下的就自行 google 吧 xD 看起来 systemd 可以符合我们想做的事,就用 systemd 来操作 sidekiq 吧!

将 Sidekiq 变成一个服务单元 (service unit)

在 systemd 中,每个服务都被视为一个「单元」(Unit) 要新增一个 sidekiq 的服务单元,我们可以新增一个档案 /lib/systemd/system/sidekiq.service (另外,用来部署 rails 的使用者叫 deployer)

# /lib/systemd/system/sidekiq.service
# 我们的 service 叫 sidekiq
[Unit]
Description=sidekiq
After=syslog.target network.target

# 这个 Type=simple 只是 systemd 要如何判断你的服务成功执行
[Service]
Type=simple
WorkingDirectory=/path/to/your/app

# 如果是用 rbenv:
# ExecStart=/bin/bash -lc 'exec /home/deploy/.rbenv/shims/bundle exec sidekiq -e production'
# 如果直接用系统安装的 ruby:
# ExecStart=/usr/local/bin/bundle exec sidekiq -e production
# 如果是用 rvm ,用 ruby 2.6.5 也无特定 gemset
# ExecStart=/home/deploy/.rvm/gems/ruby-2.6.5/wrappers/bundle exec sidekiq -e production
# 如果是用 rvm ,用 ruby 2.6.5 且有特定 gemset
# ExecStart=/home/deploy/.rvm/gems/ruby-2.6.5@gemset-name/wrappers/bundle exec sidekiq -e production
# 如果是用 rvm ,用而且专案有 .ruby-version 指定版本
ExecStart=/home/deploy/.rvm/bin/rvm in /path/to/your/app/current do bundle exec sidekiq -e production

User=deployer
Group=deployer
UMask=0002

# 这行可以大大降低 Ruby memory 用量
# 我也是抄来放上
# 不过有看 MALLOC_ARENA_MAX=2 的意思是限制 sidekiq 只能用2个 tread_pool 
# https://www.mikeperham.com/2018/04/25/taming-rails-memory-bloat/
Environment=MALLOC_ARENA_MAX=2

# 如果挂掉就重启
RestartSec=1
Restart=on-failure

# log 会记在 /var/log/syslog
StandardOutput=syslog
StandardError=syslog

# 这个服务的id 是 sidekiq
SyslogIdentifier=sidekiq

[Install]
WantedBy=multi-user.target

systemd 会去找 /lib/systemd/system/下所有的档案,所以 sidekiq.service 已经可以被 systemd 找到了。 我们接着可以执行下列指令

# 重读所有服务
sudo systemctl daemon-reload
# enable sidekiq.service 所以它可以在开机后自动运行
sudo systemctl enable sidekiq.service
# 启动 sidekiq
sudo service sidekiq start
# 在 /var/log/syslog 看有没有 sidekiq 的 log
sudo cat /var/log/syslog
# 可以用 ps 看 sidekiq 执行了或
sudo ps aux | grep sidekiq
# 或直接用 systemctl 来看目前运行中的服务
sudo systemctl status

让 Capistrano 重启 Sidekiq

我用 Capistrano 来做自动部署以避免错误。 部署完当然希望 sidekiq 重启,这样它才读到最新的程式码。 要把重启 sidekiq 加到 Capistrano 的流程中,其实只要安装 gem capistrano-sidekiq 就可以了。 在 Gemfile 加入:

# Gemfile
gem 'capistrano-sidekiq', group: :development

再到 Capfile 加入:

# Capfile
require 'capistrano/sidekiq'
# 加入 sidekiq 的 rake tasks
install_plugin Capistrano::Sidekiq
# 设定要用 systemd 去控制 sidekiq
install_plugin Capistrano::Sidekiq::Systemd

这样设定好, cap production deploy 时就会依序执行下列工作:

  • 停止从 redis 拿工作
  • 停止 Sidekiq 服务
  • 开启 Sidekiq 服务

做一个一般使用者的 sidekiq.service

上面其实我故意漏说了一件事。 上面新增的 service unit 其实是全系统范围的,也就是要用 sudo 去执行 systemctl的 如果我们希望一般的使用者也可以使用 systemd 的话,我们必须要做一个使用者自己的 sidekiq.service 而且 capistrano/sidekiq 其实预设是要用一般使用者的权限去执行 systemctl 来重启 Sidekiq 的

当然我们也可以去改 capistrano/sidekiq 的设定,让它用 root 的权限去操作 systemctl,我只是想说明其实有多种选择,每种都可以。而且一开始我的 sidekiq.service 怎麽都跑不起来就是因为我没注意到这件事....所以想特别提一下。

If you have already enabled the system-wise sidekiq.service, you need to disable it and delete the service-unit: 如果你刚刚已经安装好了全系统级别的 sidekiq.servive,你需要执行下面步骤去取消它:

# 先停止 sidekiq
sudo systemctl stop sidekiq
# 不要启用 sidekiq.service
sudo systemctl disable sidekiq.service
# 删掉
sudo rm /lib/systemd/system/sidekiq.service

一般使用者级别的服务单元要放在 ~/.config/systemd/user/ 下,systemd 会去检查下面的档案。我们再新增一次 sidekiq.service,它稍有一些不同:

[Unit]
Description=sidekiq
After=syslog.target network.target

[Service]
# 如果是用 Sidekiq 6.0.6 以后的版本,可以改成 notify 并配合 WatchdogSec
# 否则沿用 simple
Type=notify
WatchdogSec=10

WorkingDirectory=/path/to/your/app/current

# 如果是用 rbenv:
# ExecStart=/bin/bash -lc 'exec /home/deploy/.rbenv/shims/bundle exec sidekiq -e production'
# 如果直接用系统安装的 ruby:
# ExecStart=/usr/local/bin/bundle exec sidekiq -e production
# 如果是用 rvm ,用 ruby 2.6.5 也无特定 gemset
# ExecStart=/home/deploy/.rvm/gems/ruby-2.6.5/wrappers/bundle exec sidekiq -e production
# 如果是用 rvm ,用 ruby 2.6.5 且有特定 gemset
# ExecStart=/home/deploy/.rvm/gems/ruby-2.6.5@gemset-name/wrappers/bundle exec sidekiq -e production
# 如果是用 rvm ,用而且专案有 .ruby-version 指定版本
ExecStart=/home/deploy/.rvm/bin/rvm in /path/to/your/app/current do bundle exec sidekiq -e production
ExecReload=/usr/bin/kill -TSTP $MAINPID

# 这行可以大大降低 Ruby memory 用量
# 我也是抄来放上
# 不过有看 MALLOC_ARENA_MAX=2 的意思是限制 sidekiq 只能用2个 tread_pool 
# https://www.mikeperham.com/2018/04/25/taming-rails-memory-bloat/
Environment=MALLOC_ARENA_MAX=2

# 如果挂掉就重启
RestartSec=1
Restart=on-failure

# log 会记在 /var/log/syslog
StandardOutput=syslog
StandardError=syslog

# 这个服务的id 是 sidekiq
SyslogIdentifier=sidekiq

[Install]
WantedBy=default.target

我们再跑以下指令来启用/启动 sidekiq.service:

systemctl --user daemon-reload 
systemctl --user enable sidekiq.service

# 现在可以控制 sidekiq 啦
systemctl --user {start,stop,restart} sidekiq

现在 capistrano/sidekiq 应该可以正常运作了,用 capistrano 部署看看吧 : )

其它问题

部署时跑 sidekiq:quiet 出现以下错误

sidekiq:quiet
    01 systemctl --user reload sidekiq
    01 Failed to reload sidekiq.service: Job type reload is not applicable for unit sidekiq.service.
  ✘ 01 deployer@xxxxxxxx 0.067s

解法 * 加 ExecReload=/bin/kill -TSTP $MAINPID 进 sidekiq.service

出现 target 找不到

* target 其实是一组 service unit,执行 target 时全部服务会一起执行 * 用 systemctl list-units —type=target 去看能用的 target

-L log/sidekiq.log 无效

* Sidekiq 6 不再支援 log 的转向,请看 Logging · mperham/sidekiq Wiki · GitHub,一律看 /var/log/syslog * 可以用 bundle exec sidekiq 2>&1 | logger -t sidekiq 为 log 加上 sidekiq 的 tag

参考资料

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号