监视
commit信息:Correct missing semi-colon in nginx example. | 提交者:mzagaja | 提交时间:2020-07-12 | 版本:ecc51d9
在生产环境中,我推荐使用工具来监视你的Sidekiq进程,以便确保它们一直存活,并且不占用太多内存或是CPU。下面是我的推荐单:
- 使用 Systemd来开启/关闭Sidekiq。这会确保如果Ruby VM崩溃了,该程序将立即重新引导。
- Sidekiq企业版有multi-process模式,它可以监视内存使用率以及重启臃肿的worker进程。
Web UI
Sidekiq带有一个web应用,它可以显示安装的Sidekiq的当前状态。
Rails
把下面的代码添加到你的config/routes.rb
文件中:
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
注意: 简单的 mount
是不安全的 -- 任何人都可以浏览这个Web UI。请保证在你的应用中集成了Devise,Warden或者其它的验证登录库,以保护你的队列和作业。
禁止
如果你在提交一个表单时,收到一个Forbidden
错误,那是由于你没有配置有效的会话。有效的session被用来阻止CSRF攻击。你必须配置这个web app跟Rails共享相同的session。试着把下面的代码放入你的initializer:
# Rails < 4:
Sidekiq::Web.set :session_secret, Rails.configuration.secret_token
# 5.2 > Rails >= 4:
Sidekiq::Web.set :session_secret, Rails.application.secrets[:secret_key_base]
# Rails >= 5.2:
Sidekiq::Web.set :session_secret, Rails.application.credentials[:secret_key_base]
代理Web UI
如果你在Web UI前面使用nginx,要确保设置X-Script-Name
到root 位置,这样Web UI 才可以正确的构造这些必要CSS/JS资源的URLs:
location /myapp/admin/sidekiq {
proxy_set_header X-Script-Name /myapp/admin/sidekiq;
proxy_pass http://myapp.example.com/sidekiq;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Real-IP $remote_addr;
}
认证
在生产环境中,你可能想对该信息的访问进行保护。你可以使用路由(在 config/routes.rb 文件中)的约束功能来达成此目的:
Devise
允许任何经过身份验证的User
:
# config/routes.rb
authenticate :user do
mount Sidekiq::Web => '/sidekiq'
end
与以上代码相同,但是也要确保User#admin?
必须返回true
# config/routes.rb
authenticate :user, lambda { |u| u.admin? } do
mount Sidekiq::Web => '/sidekiq'
end
Clearance
Clearance提供路由约束来限制访问路由。
Blog::Application.routes.draw do
# Restricts access to all authenticated users
constraints Clearance::Constraints::SignedIn.new do
mount Sidekiq::Web, at: '/sidekiq'
end
# Restricts access to all authenticated admins
constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do
mount Sidekiq::Web, at: '/sidekiq'
end
end
Authlogic
# lib/admin_constraint.rb
class AdminConstraint
def self.matches?(request)
return false unless request.cookie_jar['user_credentials'].present?
user = User.find_by_persistence_token(request.cookie_jar['user_credentials'].split(':')[0])
user && user.admin?
end
end
# config/routes.rb
require "admin_constraint"
mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new
Authlogic 4+
由于Authlogic 4.0.0开启了安全cookies,在默认的开发者设置中,AdminConstraint从cookies读取'user_credentials'将会失败。代替这个,你可以直接从session中读取它。
# lib/admin_constraint.rb
class AdminConstraint
def matches?(request)
return false unless request.session.has_key?(:user_credentials)
user = User.find_by_persistence_token(request.session.fetch(:user_credentials))
user && user.admin?
end
end
Restful Authentication or Sorcery
检查User
实例对admin?
方法的回馈
# lib/admin_constraint.rb
class AdminConstraint
def matches?(request)
return false unless request.session[:user_id]
user = User.find request.session[:user_id]
user && user.admin?
end
end
# config/routes.rb
require 'sidekiq/web'
require 'admin_constraint'
mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new
Custom External Authentication
class AuthConstraint
def self.admin?(request)
return false unless (cookie = request.cookie_jar['auth'])
Rails.cache.fetch(cookie['user'], :expires_in => 1.minute) do
auth_data = JSON.parse(Base64.decode64(cookie['data']))
response = HTTParty.post(Auth.validate_url, :query => auth_data)
response.code == 200 && JSON.parse(response.body)['roles'].to_a.include?('Admin')
end
end
end
# config/routes.rb
constraints lambda {|request| AuthConstraint.admin?(request) } do
mount Sidekiq::Web => '/admin/sidekiq'
end
Rails使用Google认证
@jonhyman breaks down how Appboy uses Google to protect access to Sidekiq.
warden/github>Github authentication with warden/github
See Gist from the creator of the rack-based Sidekiq::Web
UI (since Sidekiq 4.2.0)
来自路由的Rails HTTP Basic Auth
# config/routes.rb
require "sidekiq/web"
Sidekiq::Web.use Rack::Auth::Basic do |username, password|
# Protect against timing attacks:
# - See https://codahale.com/a-lesson-in-timing-attacks/
# - See https://thisdata.com/blog/timing-attacks-against-string-comparison/
# - Use & (do not use &&) so that it doesn't short circuit.
# - Use digests to stop length information leaking (see also ActiveSupport::SecurityUtils.variable_size_secure_compare)
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(username), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_USERNAME"])) &
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_PASSWORD"]))
end if Rails.env.production?
mount Sidekiq::Web, at: "/sidekiq"
如果你遇到了ActionDispatch::Request::Session
错误,则说明你有Rails和Rack之间的不兼容问题。请参阅这个comment以找到解决方法。
如果你使用Warden并且全局设置failure_app
(例如在Rails项目的application.rb
文件中),你将不会看到浏览器HTTP基础提示,因为Warden会拦截401状态然后调用你的failed_app
proc。为了绕过这个,你可以像下面这样定义一些内容:
class AllowBasicAuthPrompt
def initialize(app); @app = app; end
def call(env)
# Tell Warden to not intercept the 401
env['warden'].custom_failure!; @app.call(env)
end
end
Sidekiq::Web.use AllowBasicAuthPrompt
Sidekiq::Web.use Rack::Auth::Basic do |username, password|
...
end
独立部署
这里是一个config.ru
文件例子,用来在你选择的Rack服务器中引导Sidekiq::Web:
require 'sidekiq'
Sidekiq.configure_client do |config|
config.redis = { :size => 1 }
end
require 'sidekiq/web'
run Sidekiq::Web
你也可以将sidekiq挂载到现有的Rack应用程序:
require 'your_app'
require 'sidekiq/web'
run Rack::URLMap.new('/' => Sinatra::Application, '/sidekiq' => Sidekiq::Web)
Rack session和防范web攻击
注意: Sidekiq::Web运行时需要一个有效的Rack session。如果你在Web UI点击一个按钮时得到一个了Forbidden
错误,那是因为Rack session没有被正确配置。Sidekiq不能为你配置一个session。如果你不知道如何在你的系统中设置一个有效的session,你的最佳解决方案是在StackOverflow中检索或者在那里发一个提问并且附带上你在Web UI中运行的代码。
如果你的Rails应用有通配符域,并且你想从所有域中访问Web UI,可以参考issue #2730。
如果一切正确,您应该在浏览器中看到以下内容:
独立部署并且使用基础认证
# 把代码放到你的 config.ru
require 'sidekiq'
Sidekiq.configure_client do |config|
config.redis = { :size => 1 }
end
require 'sidekiq/web'
map '/sidekiq' do
use Rack::Auth::Basic, "Protected Area" do |username, password|
# Protect against timing attacks:
# - See https://codahale.com/a-lesson-in-timing-attacks/
# - See https://thisdata.com/blog/timing-attacks-against-string-comparison/
# - Use & (do not use &&) so that it doesn't short circuit.
# - Use digests to stop length information leaking
Rack::Utils.secure_compare(::Digest::SHA256.hexdigest(username), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_USERNAME"])) &
Rack::Utils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_PASSWORD"]))
end
run Sidekiq::Web
end
Nagios
以下是包含check_sidekiq_queue脚本的nagios检测集合,它验证给定的队列纵深是否在特定的范围。它是一个使用redis-cli命令行工具的简单shell脚本,它不依赖ruby。
https://github.com/wanelo/nagios-checks
Scout
Scout,一个Rails应用监视服务,提供以下功能:
- 每个Sidekiq worker的关键指标(平均执行时间和95%的执行时间,延迟,错误率等等)。
- GitHub-enhanced 的事务跟踪,记录了单个作业的时间和内存分配。
Datedog
Datadog, 一个监视服务,提供:
Pingdom Server Monitoring
Pingdom Server Monitoring 有一个监视 Sidekiq workers的插件。
该插件可以监视入队的、失败的、以及执行完的作业数量,也包含计划作业和重试作业。你也可以为可用指标设置警告。
监视队列的滞留
你可以搭配Pingdom来使用简单的HTTP的终结点,以检查Sidekiq 'default'队列的滞留作业的大小。把下面代码放到config/routes.rb
中:
require 'sidekiq/api'
match "queue-status" => proc { [200, {"Content-Type" => "text/plain"}, [Sidekiq::Queue.new.size < 100 ? "OK" : "UHOH" ]] }, via: :get
现在当你点击 http://example.com/queue-status 时,响应的正文将是 'OK' 或者 'UHOH'。我们每分钟都会进行一个Pingdom检查,如果响应是'UHOH',则会触发一封电子邮件。
监视队列延迟
使用定制的端点
如果你把大量的作业放入队列,你可能在监视队列滞留时得到误报。相反,请监视队列延迟。队列延迟是指最早被推送到该队列的作业的推送时间与当前时间的差值。下面代码将检测任务是否花费超过30秒的排队时间。把下面代码放入config/routes.rb
文件中:
require 'sidekiq/api'
match "queue-latency" => proc { [200, {"Content-Type" => "text/plain"}, [Sidekiq::Queue.new.latency < 30 ? "OK" : "UHOH" ]] }, via: :get
现在当你点击 http://example.com/queue-latency ,这个响应主体将是 'OK' 或者 'UHOH'。
使用内建的仪表盘
Sidekiq 在/dashboard/stats
里提供了一个JSON格式的信息中心。你得到这个:
{
"sidekiq": {
"processed": 12345,
"failed": 56,
"busy": 25,
"enqueued": 178,
"scheduled": 0,
"retries": 0,
"default_latency": 12
},
"redis": {
"connected_clients": "120",
"uptime_in_days": "35",
"used_memory_human": "602.31M",
"used_memory_peak_human": "1.01G"
}
}
Sidekiq 6.0附带了一个新的sidekiqmon
二进制文件,它可以在你的终端输出基本统计信息。请使用REDIS_URL来指示sidekiqmon
到你的Redis 实例。
> sidekiqmon # uses localhost:6379
...
> REDIS_URL=redis://redis.example.com:6380/5 sidekiqmon
...