require 'capistrano/magic_recipes/base_helpers' include Capistrano::MagicRecipes::BaseHelpers
namespace :load do
task :defaults do set :monit_roles, -> { :web } set :monit_interval, -> { 60 } set :monit_bin, -> { '/usr/bin/monit' } ## Monit default: '/var/log/monit.log' set :monit_logfile, -> { "#{shared_path}/log/monit.log" } set :monit_idfile, -> { '/var/lib/monit/id' } set :monit_statefile, -> { '/var/lib/monit/state' } set :monit_downgrade_on_deploy, -> { false } ## Status set :monit_active, -> { true } set :monit_main_rc, -> { true } # set :monit_processes, -> { %w[nginx pm2 postgresql pwa redis sidekiq thin website] } set :monit_processes, -> { %w[nginx postgresql thin website] } set :monit_name, -> { "#{ fetch(:application) }_#{ fetch(:stage) }" } ## Mailer set :monit_mail_server, -> { "smtp.gmail.com" } set :monit_mail_port, -> { 587 } set :monit_mail_authentication, -> { false } # SSLAUTO|SSLV2|SSLV3|TLSV1|TLSV11|TLSV12 set :monit_mail_username, -> { "foo@example.com" } set :monit_mail_password, -> { "secret" } set :monit_mail_to, -> { "foo@example.com" } set :monit_mail_from, -> { "monit@foo.bar" } set :monit_mail_reply_to, -> { "support@foo.bar" } set :monit_ignore, -> { [] } # %w[action pid] ## Additional stuff for postrgres set :postgresql_roles, -> { :db } set :postgresql_pid, -> { "/var/run/postgresql/9.1-main.pid" } ## Additional stuff for thin (need secrets_key_base to be set) set :monit_thin_totalmem_mb, -> { 300 } ## Additional stuff for sidekiq (need secrets_key_base to be set) set :monit_sidekiq_totalmem_mb, -> { 300 } set :monit_sidekiq_timeout_sec, -> { 90 } ## Additional App helpers set :monit_app_worker_command, -> { "cd #{ current_path } ; #{fetch(:rvm_path)}/bin/rvm #{fetch(:rvm_ruby_version)} do bundle exec MONIT_CMD" } set :monit_app_worker_role, -> { :user } # user / bash / shell set :monit_app_worker_prefix, -> { :rvm } # rvm / rvm1capistrano3 / env ## WebClient set :monit_http_client, -> { true } set :monit_http_domain, -> { false } set :monit_http_port, -> { 2812 } set :monit_http_use_ssl, -> { false } set :monit_http_allow_self_certification, -> { false } set :monit_http_pemfile, -> { "/etc/monit/monit.pem" } set :monit_http_username, -> { "admin" } set :monit_http_password, -> { "monitor" } # use a subdomain for monit? set :monit_webclient, -> { false } set :monit_webclient_domain, -> { false } set :monit_webclient_use_ssl, -> { false } set :monit_webclient_ssl_cert, -> { false } set :monit_webclient_ssl_key, -> { false } set :monit_nginx_template, -> { :default } ## Website set :monit_website_check_timeout, -> { 10 } set :monit_website_check_cycles, -> { 3 } set :monit_website_check_content, -> { false } set :monit_website_check_path, -> { "/" } set :monit_website_check_text, -> { "<!DOCTYPE html>" } ## M/Monit set :monit_mmonit_url, -> { false } ## PM2 - JS - App set :monit_pm2_app_name, -> { "app" } set :monit_pm2_app_instances, -> { 1 } set :monit_pm2_app_path, -> { "/home/#{fetch(:user)}/pm2_app" } set :monit_pm2_pid_path, -> { "/home/#{fetch(:user)}/.pm2/pids" } set :monit_pm2_start_script, -> { "ecosystem.config.js" } set :monit_pm2_stage, -> { "production" } set :monit_pm2_website, -> { "example.com" } set :monit_pm2_website_ssl, -> { false } set :pm2_roles, -> { :web } set :monit_pm2_worker_role, -> { :user } set :monit_pm2_worker_prefix, -> { "" } end
end
namespace :monit do
desc "Install Monit" task :install do on release_roles fetch(:monit_roles) do execute :sudo, "apt-get update" execute :sudo, "apt-get -y install monit" end end # after "deploy:install", "monit:install" desc "Setup all Monit configuration" task :setup do on release_roles fetch(:monit_roles) do if fetch(:monit_main_rc, false) monit_config "monitrc", "/etc/monit/monitrc" end # invoke "monit:nginx" # invoke "monit:postgresql" # invoke "monit:sidekiq" # invoke "monit:redis" # invoke "monit:thin" # invoke "monit:configure_website" %w[nginx pm2 postgresql pwa redis sidekiq thin website].each do |command| invoke "monit:configure_#{command}" if Array(fetch(:monit_processes)).include?(command) end if fetch(:monit_webclient, false) && fetch(:monit_webclient_domain, false) invoke "nginx:monit:add" invoke "nginx:monit:enable" end end invoke "monit:syntax" invoke "monit:reload" end # after "deploy:setup", "monit:setup" desc 'Downgrade MONIT to 5.16 (fix action problem)' task :downgrade_system do on roles :db do execute :sudo, 'apt-get -y install monit=1:5.16-2 --allow-downgrades' end end %w[nginx pm2 postgresql redis sidekiq thin].each do |process| %w[monitor unmonitor start stop restart].each do |command| desc "#{command} monit-service for: #{process}" task "#{command}_#{process}" do if Array(fetch(:monit_processes)).include?(process) on roles(fetch("#{process}_roles".to_sym)) do if process == "sidekiq" # fetch(:sidekiq_processes) sidekiq_processes_count.times do |idx| sudo "#{fetch(:monit_bin)} #{command} #{sidekiq_service_name(idx)}" end elsif process == "thin" fetch(:app_instances).times do |idx| sudo "#{fetch(:monit_bin)} #{command} #{fetch(:application)}_#{fetch(:stage)}_thin_#{idx}" end elsif process == "pm2" fetch(:monit_pm2_app_instances).times do |idx| sudo "#{fetch(:monit_bin)} #{command} #{fetch(:application)}_#{fetch(:stage)}_pm2_#{idx}" end else sudo "#{fetch(:monit_bin)} #{command} #{process}" end end end end end if %w[nginx postgresql redis].include?(process) ## Server specific tasks (gets overwritten by other environments!) desc "Upload Monit #{process} config file (server specific)" task "configure_#{process}" do if Array(fetch(:monit_processes)).include?(process) on release_roles fetch("#{process}_roles".to_sym) do |role| monit_config( process, nil, role ) end end end elsif %w[pm2 pwa sidekiq thin].include?(process) ## App specific tasks (unique for app and environment) desc "Upload Monit #{process} config file (app specific)" task "configure_#{process}" do if Array(fetch(:monit_processes)).include?(process) on release_roles fetch("#{process}_roles".to_sym) do |role| monit_config process, "/etc/monit/conf.d/#{fetch(:application)}_#{fetch(:stage)}_#{process}.conf", role end end end end end %w[pwa website].each do |process| desc "Upload Monit #{process} config file (app specific)" task "configure_#{process}" do if Array(fetch(:monit_processes)).include?(process) on release_roles fetch("#{process == "website" ? 'nginx' : process}_roles".to_sym, :web) do |role| monit_config process, "/etc/monit/conf.d/#{fetch(:application)}_#{fetch(:stage)}_#{process}.conf", role end end end end %w[start stop restart syntax reload].each do |command| desc "Run Monit #{command} script" task command do on release_roles fetch(:monit_roles) do execute :sudo, :service, :monit, "#{command}" end end end def sidekiq_service_name(index=nil) fetch(:sidekiq_service_name, "#{fetch(:application)}_#{fetch(:stage)}_sidekiq_") + index.to_s end
end
def monit_config( name, destination = nil, role = nil )
@role = role destination ||= "/etc/monit/conf.d/#{name}.conf" template_with_role "monit/#{name}", "/tmp/monit_#{name}", @role execute :sudo, "mv /tmp/monit_#{name} #{destination}" execute :sudo, "chown root #{destination}" execute :sudo, "chmod 600 #{destination}"
end
def monit_role_prefix( role )
case role.to_s.downcase.strip when "sh", "shell" "/bin/sh -c 'REAL_COMMAND_HERE'" when "bash" "/bin/bash -c 'REAL_COMMAND_HERE'" else "/bin/su - #{fetch(:user)} -c 'REAL_COMMAND_HERE'" end
end
def monit_app_prefixed( cmd )
# fetch(:monit_app_worker_command, "cd #{ current_path } ; bundle exec MONIT_CMD").to_s.gsub(/MONIT_CMD/, cmd) komando = monit_role_prefix( fetch(:monit_app_worker_role, :user) ) case fetch(:monit_app_worker_prefix, :env).to_s.downcase.strip when "rvm" komando.gsub!(/REAL_COMMAND_HERE/, "cd #{ current_path } ; #{fetch(:rvm_path)}/bin/rvm #{fetch(:rvm_ruby_version)} do bundle exec MONIT_CMD") when "rvm1capistrano3", "rvm1capistrano", "rvm1" komando.gsub!(/REAL_COMMAND_HERE/, "cd #{ current_path } ; #{fetch(:rvm1_auto_script_path)}/rvm-auto.sh #{fetch(:rvm1_ruby_version)} bundle exec MONIT_CMD") else komando.gsub!(/REAL_COMMAND_HERE/, "/usr/bin/env cd #{current_path} ; bundle exec MONIT_CMD") end komando.gsub(/MONIT_CMD/, cmd)
end
def monit_pm2_prefixed( cmd )
# fetch(:monit_app_worker_command, "cd #{ current_path } ; bundle exec MONIT_CMD").to_s.gsub(/MONIT_CMD/, cmd) komando = monit_role_prefix( fetch(:monit_pm2_worker_role, :user) ) komando.gsub!(/REAL_COMMAND_HERE/, "cd #{fetch(:monit_pm2_app_path)} ; #{fetch(:monit_pm2_worker_prefix, '')} MONIT_CMD") komando.gsub(/MONIT_CMD/, cmd)
end
namespace :nginx do
namespace :monit do desc 'Creates MONIT WebClient configuration and upload it to the available folder' task :add => ['nginx:load_vars'] do on release_roles fetch(:nginx_roles) do within fetch(:sites_available) do config_file = fetch(:monit_nginx_template, :default) if config_file == :default magic_template("nginx_monit.conf", '/tmp/nginx_monit.conf') else magic_template(config_file, '/tmp/nginx_monit.conf') end execute :sudo, :mv, '/tmp/nginx_monit.conf', "monit_webclient" end end end desc 'Enables MONIT WebClient creating a symbolic link into the enabled folder' task :enable => ['nginx:load_vars'] do on release_roles fetch(:nginx_roles) do if test "! [ -h #{ File.join(fetch(:sites_enabled), "monit_webclient") } ]" within fetch(:sites_enabled) do execute :sudo, :ln, '-nfs', File.join(fetch(:sites_available), "monit_webclient"), "monit_webclient" end end end end desc 'Disables MONIT WebClient removing the symbolic link located in the enabled folder' task :disable => ['nginx:load_vars'] do on release_roles fetch(:nginx_roles) do if test "[ -f #{ File.join(fetch(:sites_enabled), "monit_webclient") } ]" within fetch(:sites_enabled) do execute :sudo, :rm, '-f', "monit_webclient" end end end end end
end
namespace :lets_encrypt do
desc "Generate MONIT-WebClient LetsEncrypt certificate" task :monit_certonly do on release_roles fetch(:lets_encrypt_roles) do execute :sudo, "#{ fetch(:lets_encrypt_path) }/certbot-auto --non-interactive --agree-tos --email #{fetch(:lets_encrypt_email)} certonly --webroot -w #{current_path}/public -d #{ fetch(:monit_webclient_domain).gsub(/^\*?\./, '') }" end end
end
namespace :deploy do
before :starting, :stop_monitoring do invoke "monit:downgrade_system" if fetch(:monit_downgrade_on_deploy, false) %w[sidekiq thin].each do |command| if fetch(:monit_active) && Array(fetch(:monit_processes)).include?(command) invoke "monit:unmonitor_#{command}" end end end # after :finished, :setup_monit_configs do # invoke "monit:setup" if fetch(:monit_active) # end before 'deploy:finishing', :add_monit_webclient do if fetch(:monit_webclient, false) && fetch(:monit_webclient_domain, false) invoke "nginx:monit:add" invoke "nginx:monit:enable" end end after :finished, :restart_monitoring do %w[sidekiq thin].each do |command| if fetch(:monit_active) && Array(fetch(:monit_processes)).include?(command) invoke "monit:monitor_#{command}" end end end
end
desc 'Server setup tasks' task :setup do
invoke "monit:setup" if fetch(:monit_active)
end