服務(wù)器不上個(gè)監(jiān)控總是沒有安全感抹缕,偶爾出點(diǎn)問題荔泳,追蹤起原因也不是很方便。經(jīng)過在monit盹靴、god拱她、eye之間選擇二驰,最終選擇了monit,最主要的優(yōu)點(diǎn)就是占用內(nèi)存很小秉沼,而且和ruby無關(guān)桶雀,所以也不用考慮root用戶下ruby環(huán)境的問題。
monit的安裝和簡(jiǎn)單使用就不做簡(jiǎn)單的介紹唬复,大家可以在官網(wǎng)上查看矗积。需要注意的就是建議使用root用戶來安裝monit,這樣就比較方便的使用monit啟動(dòng)其他用戶下的服務(wù)敞咧,不用考慮權(quán)限問題棘捣。
mina,部署利器休建,具體使用方法可以到官網(wǎng)查看乍恐,不再介紹。
這里主要分享丰包,使用monit+mina對(duì)服務(wù)器進(jìn)行監(jiān)控的一些監(jiān)控和部署腳本,以及踩過的坑壤巷。
部分代碼如下:
config/deploy.rb
Dir[File.dirname(__FILE__) + '/mina/*.rb'].each {|file| require file }
environments = {
'production1' => {
domain: '115.115.115.115',
branch: 'master',
env: 'production1',
path: "/var/app/wedding",
user: "root",
user_home: "/root",
rvm_home: "/usr/local/rvm",
thin_ports: 3000..3003
},
'production2' => {
domain: '222.222.222.222',
branch: 'master',
env: 'production2',
path: "/var/app/wedding",
user: "cloud",
user_home: "/home/cloud",
rvm_home: "/usr/local/rvm",
thin_ports: 2000..2007
}
}
rails_env = environments.keys.include?(ENV['RAILS_ENV']) ? ENV['RAILS_ENV'] : 'develop'
env_attrs = environments[rails_env]
branch = env_attrs[:branch]
domain = env_attrs[:domain]
rvm_home = env_attrs[:rvm_home]
rvm_path = rvm_home + "/bin/rvm"
env = env_attrs[:env]
path = env_attrs[:path]
user = env_attrs[:user]
rvm = "ruby-2.1.2"
thin_ports = env_attrs[:thin_ports]
user_home = env_attrs[:user_home]
set :rails_env, env
set :domain, domain
set :deploy_to, path
set :repository, 'git@github.com:huoshaoyun/wedding-memo-rails.git'
set :branch, branch
set :ssh_options, '-A'
set :port, '29168' if rails_env == 'production2'
set :user, user
set :term_mode, :nil
set :rvm_path, rvm_path
## script templates
## 根據(jù)template文件生成腳本邑彪,需要設(shè)置必要的變量
set :rvm_home, rvm_home
set :user_home, user_home
set :rvm, rvm
set :path, env_attrs[:path]
set :thin_ports, thin_ports
config/templates/monit/monitrc.erb
#這里是monitrc的配置,省略1000行代碼胧华。
include /etc/monit/conf.d/*
config/templates/monit/mysql.erb
記得加上as uid user and gid usergroup
寄症,這樣就可以以非root用戶啟動(dòng)服務(wù)宙彪。
check process mysqld with pidfile /var/run/mysqld/mysqld.pid
group mysql
start program = "/etc/init.d/mysql start" as uid <%= user %> and gid <%= user %>
stop program = "/etc/init.d/mysql stop" as uid <%= user %> and gid <%= user %>
if failed host 127.0.0.1 port 3306 then restart
if 5 restarts within 5 cycles then timeout
config/templates/monit/nginx.erb
check process nginx with pidfile /run/nginx.pid
start program = "/etc/init.d/nginx start" as uid <%= user %> and gid <%= user %>
stop program = "/etc/init.d/nginx stop" as uid <%= user %> and gid <%= user %>
config/templates/monit/redis.erb
# redis
check process redis with pidfile <%= user_home %>/pids/redis.pid
start = "/bin/bash -c '/bin/sh <%= user_home %>/mysh/startRedis.sh'" as uid <%= user %> and gid <%= user %>
stop program = "/etc/init.d/redis-server stop" as uid <%= user %> and gid <%= user %>
group redis
config/templates/monit/sidekiq.erb
check process sidekiq
with pidfile /var/app/wedding/shared/pids/sidekiq.pid
start program = "/etc/init.d/sidekiq start" with timeout 90 seconds
stop program = "/etc/init.d/sidekiq stop" with timeout 90 seconds
group sidekiq
config/templates/monit/thin.erb
<% thin_ports.each do |thin_port| %>
check process thin-<%= thin_port %> with pidfile /var/app/wedding/tmp/pids/thin.<%= thin_port %>.pid
start = "/bin/bash -c '/bin/sh <%= user_home %>/mysh/thin.<%= thin_port %>.sh start'" as uid <%= user %> and gid <%= user %>
stop = "/bin/bash -c '/bin/sh <%= user_home %>/mysh/thin.<%= thin_port %>.sh stop'" as uid <%= user %> and gid <%= user %>
if 3 restarts within 5 cycles then timeout
if failed port <%= thin_port %> protocol http with timeout 30 seconds for 2 cycles then restart
group thin
<% end %>
config/templates/mysh/redis-server.sh.erb
#!/bin/sh
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
# 存在bug不能正常start,只能stop
### BEGIN INIT INFO
# Provides: redis-server
# Required-Start: $syslog
# Required-Stop: $syslog
# Should-Start: $local_fs
# Should-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: redis-server - Persistent key-value db
# Description: redis-server - Persistent key-value db
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/bin/redis-server
DAEMON_ARGS=/etc/redis.conf
NAME=redis-server
DESC=redis-server
PIDFILE=<%= user_home %>/pids/redis.pid
test -x $DAEMON || exit 0
test -x $DAEMONBOOTSTRAP || exit 0
set -e
case "$1" in
start)
echo -n "Starting $DESC: "
touch $PIDFILE
chown <%= user %>:<%= user %> $PIDFILE
if start-stop-daemon --start --quiet --umask 007 --pidfile $PIDFILE --chuid <%= user %>:<%= user %> --exec $DAEMON -- $DAEMON_ARGS
then
echo "$NAME."
else
echo "failed"
fi
;;
stop)
echo -n "Stopping $DESC: "
if start-stop-daemon --stop --retry 10 --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON
then
echo "$NAME."
else
echo "failed"
fi
rm -f $PIDFILE
;;
restart|force-reload)
${0} stop
${0} start
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
這里存在一個(gè)問題不能通過這個(gè)腳本來啟動(dòng)redis有巧,只能停止redis释漆,所以又單獨(dú)寫了一個(gè)startRedis。
config/templates/mysh/startRedis.sh.erb
#!/bin/sh
cd $HOME && /usr/local/bin/redis-server $HOME/redis-2.8.16/redis.conf
config/templates/mysh/thin.sh.erb
需要export ruby環(huán)境篮迎,否則root用戶下沒有辦法來啟動(dòng)thin男图,而且export PATH="${PATH}:<%= rvm_home %>/gems/<%= rvm %>/bin:/usr/bin:<%= rvm_home %>/bin:<%= rvm_home %>/gems/<%= rvm %>@global/bin:<%= rvm_home %>/rubies/<%= rvm %>/bin/"
要將${PATH}
放到前面,這樣就不會(huì)被系統(tǒng)默認(rèn)的ruby環(huán)境覆蓋甜橱。
#!/bin/bash
# Set the environment, as required by Monit
export PATH="${PATH}:<%= rvm_home %>/gems/<%= rvm %>/bin:/usr/bin:<%= rvm_home %>/bin:<%= rvm_home %>/gems/<%= rvm %>@global/bin:<%= rvm_home %>/rubies/<%= rvm %>/bin/"
export GEM_PATH="<%= rvm_home %>/gems/<%= rvm %>:<%= rvm_home %>/gems/<%= rvm %>@global"
export GEM_HOME="<%= rvm_home %>/gems/<%= rvm %>"
start () {
cd <%= path %>/current
BUNDLE_GEMFILE=<%= path %>/current/Gemfile bundle exec thin -C /etc/thin/wedding.<%= thin_port %>.yml -d start
}
stop () {
kill -s QUIT $(cat <%= deploy_to %>/tmp/pids/thin.<%= thin_port %>.pid)
}
case $1 in
start)
start
;;
stop)
stop
;;
*)
echo $"Usage: $0 {start|stop}"
exit 1
;;
esac
exit 0
config/templates/thin.yml.erb
---
chdir: /var/app/wedding/current
environment: <%= rails_env %>
address: 0.0.0.0
port: <%= thin_port %>
timeout: 30
log: /var/app/wedding/shared/log/thin.log
pid: /var/app/wedding/tmp/pids/thin.pid
max_conns: 1024
max_persistent_conns: 100
require: []
wait: 30
servers: <%= servers_number %>
daemonize: true
config/mina/base.rb
require 'erb'
def template(from, to)
queue %{echo "-----> Put #{from} file to #{to}"}
erb = File.read(File.expand_path("../../templates/#{from}", __FILE__))
put ERB.new(erb).result(binding), to
end
def put(content, file)
queue %[echo #{escape content} > "#{file}"]
end
def escape(str)
Shellwords.escape(str)
end
config/mina/mysh.rb
namespace :mysh do
task :setup do
invoke :'mysh:thin'
invoke :'mysh:sidekiq'
invoke :'mysh:redis-server' if rails_env == 'production2'
%w[startRedis.sh].each do |name|
destination = "$HOME/mysh/#{name}"
template "mysh/#{name}.erb", destination
queue "chmod a+x #{destination}"
end
end
task :thin do
thin_ports.each do |thin_port|
set :thin_port, thin_port
destination = "$HOME/mysh/wedding.#{thin_port}.sh"
template "mysh/thin.sh.erb", destination
queue "chmod a+x #{destination}"
end
end
task :sidekiq do
destination = "$HOME/mysh/sidekiq.sh"
template "mysh/sidekiq.sh.erb", destination
queue "chmod a+x #{destination}"
queue "sudo cp #{destination} /etc/init.d/sidekiq"
end
task "redis-server" do
destination = "$HOME/mysh/redis-server.sh"
template "mysh/redis-server.sh.erb", destination
queue "chmod a+x #{destination}"
queue "sudo cp #{destination} /etc/init.d/redis-server"
end
end
通過mina mysh:setup
來生成服務(wù)器相關(guān)的啟動(dòng)腳本逊笆。
config/mina/thin.rb
namespace :thin do
task :setup do
thin_ports.each do |thin_port|
destination = "/etc/thin/wedding.#{thin_port}.yml"
set :thin_port, thin_port
set :servers_number, 1
template "thin.yml.erb", destination
end
invoke :'thin:railsapp'
end
task "railsapp" do
destination = "/etc/thin/railsapp.yml"
set :thin_port, thin_ports.first
set :servers_number, thin_ports.size
template "thin.yml.erb", destination
end
end
通過mina thin:setup
來生成thin的配置文件。
config/mina/monit.rb
namespace :monit do
desc "Setup all Monit configuration"
task :setup do
queue %{echo "-----> Setting up Monit..."}
monit_config "monitrc", "/etc/monit/monitrc"
monit_config "monitrc", "/etc/monitrc"
invoke :'monit:mysql'
invoke :'monit:redis' if rails_env == 'production2'
invoke :'monit:nginx'
invoke :'monit:thin'
invoke :'monit:sidekiq'
end
after "deploy:setup", "monit:setup"
task(:nginx) { monit_config "nginx" }
task(:mysql) { monit_config "mysql" }
task(:redis) { monit_config "redis" }
task(:thin) { monit_config "thin" }
task(:sidekiq) { monit_config "sidekiq" }
%w[start stop syntax reload].each do |command|
desc "Run Monit #{command} script"
task command do
queue %{echo "-----> Monit #{command}"}
queue "sudo service monit #{command}"
end
end
task :status do
queue "sudo monit status"
end
end
def monit_config(name, destination = nil)
destination ||= "/etc/monit/conf.d/#{name}"
template "monit/#{name}.erb", "$HOME/monit/#{name}"
queue "sudo cp $HOME/monit/#{name} #{destination}"
queue "sudo chown root #{destination}"
queue "sudo chmod 600 #{destination}"
end
通過mina monit:setup
來生成monit的配置文件岂傲。 這里需要注意难裆,一定要將monit的一些配置文件的owner修改為root,即和monit安裝用戶是同一用戶镊掖,否則是無法啟動(dòng)的乃戈。 另外需要注意的是,需要考慮各項(xiàng)服務(wù)的pid文件亩进,某些service是默認(rèn)關(guān)閉pid文件的症虑,需要修改相應(yīng)的配置文件打開。
將配置文件和腳本模版化镐侯,可以在配置完一臺(tái)服務(wù)器后侦讨,非常迅速的配置完成其他服務(wù)器。
一切搞定苟翻,這樣就可以隨時(shí)查看服務(wù)器的狀態(tài)韵卤,來點(diǎn)效果圖。
![](https://ruby-china-files.b0.upaiyun.com/photo/2014/6b441c0ceb1345ead74db110abcddf04.png)
![](https://ruby-china-files.b0.upaiyun.com/photo/2014/275c2d9a898c3e9f0898cf6dcae55e12.png)
試著kill掉一個(gè)thin
![](https://ruby-china-files.b0.upaiyun.com/photo/2014/e3bf5b405e53a941cd024533d7f92276.png)
:smiley: