ブログトップ 記事一覧 ログイン 無料ブログ開設

IT 東京 楽しいと思うこと

2013-06-08

サーバのリソース使用状況レポートを作る

12:16 |

数百台のサーバに対して

の使用状況をサクッとチェックしたいなーと思ったのですが、さすがにmuninのグラフで見るのはダルすぎる。

というわけで日次でこういうページを作ってチェックするようにしました。

f:id:mikeda:20130607084719p:image

上記の情報が数字でダーっと並んでて、ついでに簡単に色付けとか、muninへのリンク張りとか、各項目でのソート機能付けたりとかをやってます。

CPUとメモリの使用率は前日の平均、ディスク使用率はバッチ実行時の値です。


最初はmuninのRRDファイルから作ろうかと思ったのですが(gist)、この程度の情報ならsysstatやdfの結果から作るほうが簡単なので、sshで集めてくることにしました。

とりあえずHTMLに出力してますが、CSVで出したりDBに突っ込んだりすれば各種調査に便利ですよ!


ソースコード

Ruby1.9版です

#!/usr/local/bin/ruby
require 'net/ssh'
require 'json'
require 'erb'
require 'pp'

server_list_file = "/home/mikeda/check_servers/sv.list" # このファイルに調査対象のホストを記載
erb_template_file = "/home/mikeda/check_servers/resource.html.erb"

def summarize_cpu(sar_cpu)
  _, _, user, nice, system, iowait, steal, idle = sar_cpu.split(/\s+/)
  {
    user:   user.to_f,
    nice:   nice.to_f,
    system: system.to_f,
    iowait: iowait.to_f,
    steal:  steal.to_f,
    idle:   idle.to_f
  }
end

def summarize_memory(sar_memory)
  _, kbmemfree, kbmemused, memused, kbbuffers, kbcached, kbcommit, commit = sar_memory.split(/\s+/)
  mem = {
    kbmemfree: kbmemfree.to_i,
    kbmemused: kbmemused.to_i,
    memused:   memused.to_i,
    kbbuffers: kbbuffers.to_i,
    kbcached:  kbcached.to_i,
    kbcommit:  kbcommit.to_i,
    commit:    commit.to_f
  }
  total = ( mem[:kbmemfree] + mem[:kbmemused] ) / 1024
  used = ( mem[:kbmemused] - mem[:kbbuffers] - mem[:kbcached] ) / 1024
  {
    total: total,
    used:  used,
    usage: (100 * used.to_f / total).to_i
  }
end

def summarize_disk(df)
  disk = {}
  df.split("\n").each do |d|
    filesystem, size, used, avail, usage, mount = d.split(/\s+/)
    disk[mount] = {
      filesystem: filesystem,
      size:       size.to_i,
      used:       used.to_i,
      avail:      avail.to_i,
      usage:      usage.chop.to_i
    }
  end

  # /varが個別にマウントされていればその情報、それ以外は/の情報を返す
  disk['/var'] || disk['/']
end


servers = open(server_list_file).readlines.map(&:chomp)
resources = {}
YESTERDAY_SAR = "/var/log/sa/sa#{(Date.today - 1).strftime("%d")}"
servers.each do |host|
  begin
    Net::SSH.start(host, 'mikeda') do |ssh|
      resources[host] = {}

      sar_cpu = ssh.exec!("LANG=C sar -f #{YESTERDAY_SAR} | tail -1")
      resources[host][:cpu] = summarize_cpu(sar_cpu)

      sar_memory = ssh.exec!("LANG=C sar -r -f #{YESTERDAY_SAR} | tail -1")
      resources[host][:memory] = summarize_memory(sar_memory)

      df = ssh.exec!("LANG=C df -lmP | sed 1d")
      resources[host][:disk] = summarize_disk(df)
    end
  rescue => e
    STDERR.puts "#{host}: #{e.message}"
  end
end


### HTML出力用
def color(value, warn, critical)
  if value > critical
    'red'
  elsif value > warn
    'yellow'
  else
    ''
  end
end
def color_cpu_usr(usage); color(usage, 30, 60) end
def color_cpu_sys(usage); color(usage, 5,  10) end
def color_cpu_io(usage);  color(usage, 5,  10) end
def color_memory(usage);  color(usage, 60, 80) end
def color_disk(usage);    color(usage, 60, 80) end

puts ERB.new(File.read(erb_template_file)).result


### JSON出力用
#puts JSON.pretty_generate(resources)

とりあえずコイツをcronで毎日実行してます

7 7 * * * /home/mikeda/check_servers/check_server_resource.rb > /var/www/html/files/check_servers/resource/resource_`date +\%Y\%m\%d`.html

ERBテンプレート

各項目でソートできるようにコレ使ってます

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>サーバリソースチェック</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="/files/js/joequery-Stupid-Table-Plugin/stupidtable.min.js"></script>
<script type="text/javascript">
$(function(){
        $("table").stupidtable();
});
</script>
</style>
</head>
<body>
<table border="1">
<thead>
<tr>
<th data-sort="string">Host</th>
<th data-sort="float">user</th>
<th data-sort="float">system</th>
<th data-sort="float">io</th>
<th data-sort="float">idle</th>
<th data-sort="int">メモリ使用率</th>
<th data-sort="int">ディスク使用率</th>
</tr>
</thead>
<% resources.each do |host, r| %>
<tr>
  <td><a href="http://munin.mikeda.jp/server/<%= host %>/index.html#system"><%= host %></a></td>
  <td align="right" bgcolor="<%= color_cpu_usr(r[:cpu][:user]) %>"><%= r[:cpu][:user] %></td>
  <td align="right" bgcolor="<%= color_cpu_sys(r[:cpu][:system]) %>"><%= r[:cpu][:system] %></td>
  <td align="right" bgcolor="<%= color_cpu_io(r[:cpu][:iowait]) %>"><%= r[:cpu][:iowait] %></td>
  <td align="right" ><%= r[:cpu][:idle] %></td>
  <td align="right" bgcolor="<%= color_memory(r[:memory][:usage]) %>"><%= r[:memory][:usage] %>%</td>
  <td align="right" bgcolor="<%= color_disk(r[:disk][:usage]) %>"><%= r[:disk][:usage] %>%</td>
</tr>
<% end %>
</table>
</body>
</html>


なんでもかんでもmuninやZabbixのグラフで見ようとするのは非効率的ですよ!

2013-06-05

ログの差分出力スクリプト

07:13 |

昔Perlで書いたやつRuby1.9版をとりあえず作りました

ログファイルを指定すると、前回実行からの差分を出力してくれます

$ echo 111 > test.log
$ ./logtail.rb test.log
111

$ echo 222 >> test.log
$ echo 333 >> test.log
$ ./logtail.rb test.log
222
333

ソースコード

#!/usr/local/bin/ruby

# ログの差分出力スクリプト
# 使用例
#   前回実行からの追記分を出力する
#   $ logtail.rb access_log
#
#   ログのローテートに対応させる場合はオプションでローテートファイルを指定する
#   $ logtail.rb access_log -r access_log.1
#
#   デフォルトでは/tmp/<ログファイル名>.logtail.offsetに前回出力情報が保存される。
#   日付つきファイルなどファイル名が変わる場合、
#   同じファイルを複数バッチから処理する場合はこのファイルパスを個別に指定すること
#   $ logtail.rb access_log.20130602 -r access_log.20130601 -o /tmp/test_access_log.logtail.offset
#
# TODO:オフセットファイルの作成、書き込み権限のチェック
# TODO:ファイル名が同じだとデフォルトのオフセットファイル名が重複する。フルパスのハッシュ値とかつけたほうがいいかな

require 'optparse'
require 'pp'

options = {}
OptionParser.new do |opt|
  opt.on('-r', '--rotated_log=VAL') {|v| options[:rotated_log] = v}
  opt.on('-o', '--offset_file=VAL') {|v| options[:offset_file] = v}
  opt.parse!
end

log = ARGV.shift
exit 1 unless log


class LogTail
  def initialize(log, options)
    @log = log
    @rotated_log = options[:rotated_log]
    @offset_file = options[:offset_file] || "/tmp/#{File.basename(log)}.logtail.offset"
    @offset = load_offset
  end

  def load_offset
    return nil unless File.exist? @offset_file

    line = File.read(@offset_file)
    ino, pos = line.split(',')
    { ino: ino.to_i, pos: pos.to_i}
  end

  def save_offset(ino, pos)
    File.write(@offset_file, "#{ino},#{pos}")
  end
  
  def tail_print(log, pos)
    file = File.open(log)
    file.seek(pos)
    file.each{|line| puts line}
    end_pos = file.pos
    file.close
  
    return end_pos
  end
  
  def tail_rotated_log
    return unless @offset && @rotated_log && File.exists?(@rotated_log)

    stat = File.stat(@rotated_log)
    if @offset[:ino] == stat.ino && @offset[:pos] < stat.size
      end_pos = tail_print(@rotated_log, @offset[:pos])
      save_offset(stat.ino, end_pos)
    end
  end

  def tail_log
    return unless @log && File.exists?(@log)

    stat = File.stat(@log)
    if @offset && @offset[:ino] == stat.ino
      if @offset[:pos] < stat.size
        start_pos = @offset[:pos]
      elsif @offset[:pos] > stat.size
        save_offset(stat.ino, stat.size)
        return
      else
        return
      end
    else
      start_pos = 0
    end
    end_pos = tail_print(@log, start_pos)
    save_offset(stat.ino, end_pos)
  end

  def execute 
    tail_rotated_log
    tail_log
  end
end
  
LogTail.new(log, options).execute

muninやnagiosとかと連携して、各種ログの監視、集計結果のグラフ化などに使えます。

例えばこういうふうにApacheの平均レスポンスタイムをmuninでグラフ化したい場合、

f:id:mikeda:20130605063727p:image


httpd.confのLogFormatにレスポンスタイムを追加して、

  # %Dを追加
  LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined

 # cronologやrotatelogsで日付ファイルに出力してる場合
  CustomLog "|/usr/sbin/cronolog -S /var/log/httpd/access_log.latest /var/log/httpd/access_log.%Y%m%d" combined

こういうmuninプラグインを作ればOKです

#!/bin/bash

today=$(date +%Y%m%d)
yesterday=$(date +%Y%m%d -d "1 days ago")
log=/var/log/httpd/access_log.$today
rotated_log=/var/log/httpd/access_log.$yesterday
offset_file=/tmp/munin_app_response_time.logtail.offset

if [ "$1" = "config" ]; then
   echo 'graph_title response time'
   echo 'graph_args --base 1000 -l 0'
   echo 'graph_vlabel msec'
   echo 'graph_scale no'
   echo 'graph_category app'
   echo 'avg.label avg'
   exit 0
fi

# TODO:muninをがしばらく停止すると次回実行時に大きな値が出てしまうので、オフセットファイルのタイムスタンプをチェックする

if [ -e "$offset_file" ];then
  avg_msec=$(/usr/local/bin/logtail.rb $log -r $rotated_log -o $offset_file | perl -alne '$c++;$s+=$F[-1];END{print int(($s/$c)/1000)}')
else
  # 初回は集計せずに捨てる
  /usr/local/bin/logtail.rb $log -r $rotated_log -o $offset_file > /dev/null
  avg_msec='N'
fi
echo "avg.value $avg_msec"

仕組みや実装上、あまり厳密な集計には向かないですがこういうグラフ作成とかログ監視には充分かな

2013-05-25

CLIでNagiosのアラートを停止する

19:00 |

先日、こんなことをつぶやいたところ、

@hirose31さんから親切なアドバイスをいただきました。

使ってみるとなかなか便利だったので紹介しておきます!


Nagiosの外部コマンド機能について

名前付きパイプにコマンドを書き込むことで、Nagiosに命令を送る機能があるようです。

『外部コマンド』

コマンドのリストや書式、設定については上記リンクで確認して下さい。


EPELからインストールしたNagiosだとデフォルトで有効化されていました。

# nagios.cfg
check_external_commands=1
command_check_interval=-1
command_file=/var/spool/nagios/cmd/nagios.cmd
external_command_buffer_slots=4096

というわけで、特定ホストのサービス監視のアラート停止は以下のコマンドでできます。

[root@nagios01 ~]# echo "[$(date +%s)] DISABLE_HOST_SVC_NOTIFICATIONS;test01" > /var/spool/nagios/cmd/nagios.cmd

バッチリ通知が止まりました!!!

f:id:mikeda:20130525185415p:image:w640

もどしはENABLE_HOST_SVC_NOTIFICATIONS、ホスト監視も停止する場合はDISABLE_HOST_NOTIFICATIONSを合わせて使います。

簡単ですね!!!


せっかくなので複数ホストのアラートを完全に無効化するスクリプトを作ってみました

ソースコード

#!/bin/bash

action=$1;shift
hosts=$@
command_file="/var/spool/nagios/cmd/nagios.cmd"

if [ "$action" = start ];then
  commands="ENABLE_HOST_NOTIFICATIONS ENABLE_HOST_SVC_NOTIFICATIONS"
elif [ "$action" = stop ];then
  commands="DISABLE_HOST_NOTIFICATIONS DISABLE_HOST_SVC_NOTIFICATIONS"
else
  exit
fi

now=$(date +%s)
for host in $hosts;do
  for command in $commands;do
    echo "[$now] $command;$host"
  done
done > $command_file

※エラー処理を追加したものをここにアップしました


使い方はこんな感じです

[root@nagios01 ~]# ./nagios_alert.sh stop test01 test02
[root@nagios01 ~]# ./nagios_alert.sh start test01 test02

f:id:mikeda:20130525185416p:image:w640

(゚∀゚)キタコレ!!


これで作業がちょっと楽になりそうです。

@hirose31さん、ありがとうございました!

2013-04-14

『qpstudy3周年記念LT大会』でLTしてきた!

16:37 |

『qpstudy3周年記念LT大会 〜新人さん、業界にようこそ!〜 with ビール』に参加&LTしてきました!


14時から始まって簡単なメインセッションの後、20人くらいのLTが19時近く?まで!

いやー、さすがに飲み疲れましたw


自分のLT資料です


今回のやりたかったのはこのへんでした

  • なにか新人さんが参考になる話をする
  • ライブオペレーション

ライブオペレーションについては、開発系の勉強会の『ライブコーディング』と同じ感じで、

  • みんなでだれかのオペレーションを見て
  • それについていろいろ議論する

と楽しいかなーと。

初挑戦だったので、テキパキ行かずに何やってるかうまく伝えられなかったですが、なかなか楽しかったのでまたやりたいと思います!


あとはライブオペレーションでやったことについて、ザッと解説しておきます!


ライブオペレーションの手順書

お題は趣味構築中のサーバ管理Railsアプリです。

サーバミドルウェアアプリの構築、外部公開まで一通りのオペレーションをやりました。


さっき通しでやってみたら全部で10分くらいでした。

※発表の時は、時間のかかるとこは先に完了済みサーバを準備しておいて切り替える、QP3分クッキング方式を使いました。

VM作成

シェルスクリプト叩くだけです(細かい仕組みは後で補足解説します)

ssh vm-host02
sudo su -
sh vm_create.sh qpstudy01 192.168.1.151

インストーラが起動してOSインストールされます

f:id:mikeda:20130414145245p:image:w450

ローカルDNSの登録、OSの初期設定、chef実行準備もついでに完了しちゃいます


OS設定、ミドルウェアのセットアップ

サーバが起動したらchef-soloを実行してミドルウェアをセットアップします

ssh qpstudy01
sudo su - sysadmin
vim ~/chef/nodes/qpstudy01.json
sudo /home/sysadmin/.rbenv/shims/chef-solo -c ~/chef/solo/solo.rb -j ~/chef/nodes/qpstudy01.json

今回のnode定義JSONはこんな感じ

{
  "run_list": [
    "role[default]",
    "recipe[rbenv::mikeda]",
    "recipe[mysql::server]"
  ]
}

このへんが実行されます。

  • 共通設定(OSの初期設定、管理/監視ツールのインストールなど)
  • ruby(rbenv)のセットアップ
  • MySQLのセットアップと起動

※問題なければjsonファイルをgithubにpushしちゃいますが、そのへんの運用はknife-solo的な中央管理に見直し検討中です。


自宅環境で使っているcookbookはgithubで公開してるので、細かいところはそっちを見て下さい。

https://github.com/mikeda/chef


アプリの構築と起動

githubからアプリをcloneしてセットアップします。

ssh qpstudy01
git clone https://github.com/mikeda/sabakan
cd sabakan

# 依存gemのインストール
bundle install

# DBのセットアップ
bundle exec rake db:create
bundle exec rake db:migrate

# サーバ起動
# 今回はwebrickで起動します。(ちゃんと外部公開する時はunicornとかpassengerを使いましょう)
bundle exec rails server
外部DNSドメイン登録

Route53使ってるので、AWSのManagementConsoleから登録します

f:id:mikeda:20130414145244p:image:w450


外部公開用リバースプロキシの設定

登録したドメインへのアクセスを、構築したサーバに転送するように設定します。

ssh proxy01
sudo su -
vim /etc/nginx/conf.d/qpstudy.conf
/etc/init.d/nginx configtest
/etc/init.d/nginx reload

今回のコンフィグはこんな感じです

server {
    server_name  qpstudy.mikeda.jp;
    access_log  /var/log/nginx/qpstudy.mikeda.jp_access.log  main;

    location / {
        proxy_pass   http://192.168.1.151:3000;

        proxy_set_header        Host                $host;
        proxy_set_header        X-Real-IP           $remote_addr;
        proxy_set_header        X-Forwarded-Host    $host;
        proxy_set_header        X-Forwarded-Server  $host;
        proxy_set_header        X-Forwarded-For     $proxy_add_x_forwarded_for;
    }
}

これで完了!外部のブラウザからアクセスできるようになります。

f:id:mikeda:20130414162353p:image:w450

簡単ですねー

(本番ではプロキシ設定ミスって502エラーでしたw)


VM作成部分の補足解説

VM作成スクリプトの中身はこんな感じです

#!/bin/bash

HOSTNAME=$1
IP=$2
LOCATION="http://192.168.1.110/mrepo/centos6-x86_64/disc1/"
TYPE=centos6_kvm
VCPUS=1
RAM=1024
DISK=5

IMG=/data/vm/${HOSTNAME}.img

if [ -f "$IMG" ];then
  echo already exists
  exit 1
fi

### PowerDNSにレコード登録
mysql -umikeda -pmikedapass -hdns01 pdns -e "INSERT INTO records (domain_id, name, content, type, ttl, prio) VALUES (1, '${HOSTNAME}.mikeda.jp', '${IP}', 'A', 60, NULL);"

### VMイメージファイル作成
dd if=/dev/zero of=$IMG bs=1MiB count=`expr $DISK \* 1024`

### VMインストール
virt-install  --connect qemu:///system \
  --name $HOSTNAME \
  --ram $RAM \
  --vcpus=$VCPUS \
  --disk path=$IMG \
  --os-type=linux \
  --os-variant=virtio26 \
  --accelerate \
  --nographics \
  --location="${LOCATION}" \
  --extra-args="ks=http://192.168.1.110/ks/gen/ks.php?cfg=${TYPE}.cfg+${HOSTNAME}+${IP} console=tty0 console=ttyS0,115200n8"

内部DNSサーバMySQLバックエンドのPowerDNS)にホストを登録するのも合わせてやっています。


virt-installで指定するkickstartファイルは動的に生成しています。

このPHPの中身はこうなってます。

<?php
list($cfg, $hostname, $ip) = explode(" ", $_GET["cfg"]);

$ks_config = file_get_contents($cfg);
echo preg_replace(
  "/^network.*/m",
  "network --device eth0 --bootproto static "
    . "--ip $ip --netmask 255.255.255.0 --gateway 192.168.1.1 "
    . "--nameserver 192.168.1.103 "
    . "--hostname $hostname",
  $ks_config
);

ベースのkickstartファイルのnetwork行を書き換えてるだけですね。

URLを叩くと、

curl 'http://192.168.1.110/ks/gen/ks.php?cfg=centos6_kvm.cfg+qpstudy01+192.168.1.151'

こういうkickstartファイルが帰ってきます

install
text
url --url=http://192.168.1.110/mrepo/centos6-x86_64/disc1/
lang ja_JP.UTF-8
keyboard jp106
network --device eth0 --bootproto static --ip 192.168.1.151 --netmask 255.255.255.0 --gateway 192.168.1.1 --nameserver 192.168.1.103 --hostname qpstudy01
rootpw  --iscrypted XXXXXXXXXXXXXXXXXX
firewall --service=ssh
authconfig --enableshadow --passalgo=sha512
selinux --disabled
timezone Asia/Tokyo
reboot

zerombr
bootloader --location=mbr --driveorder=vda --append="console=ttyS0,115200n8"
clearpart --all --initlabel --drives=vda
part /boot --fstype ext4 --size=100 --ondisk=vda
part swap --size=1024
part / --fstype ext4 --size=1 --grow

%packages
@base
@core
@debugging
@development
@japanese-support
@performance
@server-policy
sgpio
systemtap-client
telnet
openssl-devel
readline-devel
zlib-devel
nc

%pre
#/bin/sh
/usr/bin/wget http://192.168.1.110/setup/os_setup_centos6.sh -O /tmp/os_setup_centos6.sh

%post
%include /tmp/os_setup_centos6.sh

kickstartの%postで%includeしているスクリプトで、最低限のOS設定とchefのセットアップを実施しています。

#!/bin/sh

### ユーザ追加
useradd -u 999 sysadmin
useradd -u 1000 mikeda
sed -i 's|^mikeda:.*$|mikeda:$6$BovFTzjb$Eoa5XXXXXXXwK5e1:15627:0:99999:7:::|' /etc/shadow

mkdir /home/mikeda/.ssh
cat > /home/mikeda/.ssh/authorized_keys <<END
ssh-rsa AAAAB3NzaCXXXXXXXXXXX78rrAJbw== mikeda
END
chown -R mikeda. /home/mikeda/.ssh
chmod 700 /home/mikeda/.ssh
chmod 600 /home/mikeda/.ssh/*

### sudo設定
sed -i '/^Defaults *requiretty/s/^/#/' /etc/sudoers
echo 'mikeda ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
echo 'sysadmin ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

### リゾルバ設定
cat <<END >/etc/resolv.conf
nameserver 192.168.1.103
nameserver 192.168.1.1
search mikeda.jp
END

### sshd設定
sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
echo "sshd:192.168.1.0/255.255.255.0,127.,LOCAL" >> /etc/hosts.allow
echo "sshd:ALL" >> /etc/hosts.deny

### iptablesの停止
chkconfig ip6tables off
chkconfig iptables off

### 不必要なservice停止
chkconfig atd off
chkconfig auditd off
chkconfig autofs off
chkconfig cups off
chkconfig smartd off

### IPV6無効化。Railsがエラーになるのでいったん完全無効化してない
#cat <<END >> /etc/modprobe.d/ipv6.conf
#options ipv6 disable=1
#END
echo 'NETWORKING_IPV6=no' >> /etc/sysconfig/network

### SELinuxをdisる
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

### 時刻合わせ。意味ないかも。
/usr/sbin/ntpdate 210.173.160.27

### YUMレポジトリの追加
# EPEL
rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

# mrepoで作成したローカルレポジトリの追加
cat > /etc/yum.repos.d/local.repo <<'END'
[freeze]
name=CentOS-$releasever - Freeze
baseurl=http://192.168.1.110/mrepo/centos6-x86_64/RPMS.freeze/
gpgcheck=0
enabled=1

[test]
name=CentOS-$releasever - Freeze
baseurl=http://192.168.1.110/mrepo/centos6-x86_64/RPMS.test/
gpgcheck=0
enabled=0
END

### 必須パッケージのインストール
yum -y install git

### chef-clientのセットアップ。RPM版に変えるかも
cat  > /tmp/install_chef-client.sh <<'END'
wget http://192.168.1.110/files/ruby/rbenv_chef_centos6.tgz -P /tmp/
tar xzf /tmp/rbenv_chef_centos6.tgz
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
git clone git://github.com/mikeda/chef.git
END
chmod 755 /tmp/install_chef-client.sh
su - sysadmin -c /tmp/install_chef-client.sh

完了すると自動でサーバ再起動されて、sshログインできるようになってます。

[mikeda@admin02 ~]$ ssh qpstudy01
Warning: Permanently added 'qpstudy01,192.168.1.151' (RSA) to the list of known hosts.
[mikeda@qpstudy01 ~]$ 

まとめ

みんなライブオペレーションやろうよ!

2013-03-31

rsyslogでNW機器のログ集約

14:22 |

NW機器のログはsyslogサーバに集約してWEBで見れるようにしてます

理由はこのへんです。

  • いちいちログインして見るのがダルい
  • 障害時にログインして確認しようにも入れないことが多い(だってNW機器ですもん)
  • 仕方ないので再起動するとログ消えちゃう

見ためはこんな感じ(サンプル

サーバ一覧

f:id:mikeda:20130331140155p:image:w450

日付ごとのログ一覧

f:id:mikeda:20130331140156p:image:w450

ログの中身

f:id:mikeda:20130331140157p:image:w450


ずっとsyslog-ng派だったのですが、時代の流れに逆らえずrsyslogを使ってみたので手順をメモっておきます。

環境はCentOS6 & rsyslog5.8です。


手順

/etc/rsyslog.confを編集
# モジュール読み込み。UDP追加
$ModLoad imuxsock
$ModLoad imklog
$ModLoad imudp

# Apacheユーザで読めるようにパーミッション設定
$umask 0000
$DirCreateMode 0755
$FileCreateMode 0644

# ログのフォーマット、パス定義
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$template RemoteLog, "/var/log/remote/%hostname:::secpath-replace%/%hostname:::secpath-replace%_%$year%%$month%%$day%.log" 

# ローカルログ用のルール設定
$RuleSet local
*.info;mail.none;authpriv.none;cron.none                /var/log/messages
authpriv.*                                              /var/log/secure
mail.*                                                  -/var/log/maillog
cron.*                                                  /var/log/cron
*.emerg                                                 *
uucp,news.crit                                          /var/log/spooler
local7.*                                                /var/log/boot.log

# リモートsyslog用のルール設定
$RuleSet remote
*.* ?RemoteLog

$DefaultRuleset local

# リモートsyslogの受信設定
$AllowedSender UDP, 127.0.0.0/8, 192.168.0.0/16
$InputUDPServerBindRuleset remote
$UDPServerRun 514
rsyslog再起動
# /etc/init.d/rsyslog restart
Apache設定
# yum install httpd
# vi /etc/httpd/conf.d/remote_log.conf 
Alias /remote_log/ /var/log/remote/
<Directory /var/log/remote/>
  Order deny,allow
  Deny from all
  Allow from 127.0.0.0/8
  Allow from 192.168.0.0/16
  Options +Indexes
</Directory>
# /etc/init.d/httpd start

という感じでサクッとでけました。



rsyslog使ってみた感想

慣れの問題が大きいと思いますが、ちょっと設定がわかりづらく感じました。

・同じ事をするための書式がいろいろある

・各設定の境目とつながりがよくわからない

syslog-ngで同じことをやろうとするとこんな設定になると思います(最後の3行以外ほぼデフォルト

options {
        sync (0);
        time_reopen (10);
        log_fifo_size (1000);
        long_hostnames (off);
        use_dns (yes);
        use_fqdn (no);
        create_dirs (yes);
        keep_hostname (no);
        stats(86400);
        dir_perm(0755);
        perm(0644);
        owner(root);
        group(root);
};

### syslogdのデフォルト設定設定を引き継ぐ
source s_sys {
        file ("/proc/kmsg" log_prefix("kernel: "));
        unix-stream ("/dev/log");
        internal();
        # udp(ip(0.0.0.0) port(514));
};

destination d_cons { file("/dev/console"); };
destination d_mesg { file("/var/log/messages"); };
destination d_auth { file("/var/log/secure"); };
destination d_mail { file("/var/log/maillog" sync(10)); };
destination d_spol { file("/var/log/spooler"); };
destination d_boot { file("/var/log/boot.log"); };
destination d_cron { file("/var/log/cron"); };
destination d_kern { file("/var/log/kern"); };
destination d_mlal { usertty("*"); };

filter f_kernel     { facility(kern); };
filter f_default    { level(info..emerg) and
                        not (facility(mail)
                        or facility(authpriv)
                        or facility(cron)); };
filter f_auth       { facility(authpriv); };
filter f_mail       { facility(mail); };
filter f_emergency  { level(emerg); };
filter f_news       { facility(uucp) or
                        (facility(news)
                        and level(crit..emerg)); };
filter f_boot   { facility(local7); };
filter f_cron   { facility(cron); };

#log { source(s_sys); filter(f_kernel); destination(d_cons); };
log { source(s_sys); filter(f_kernel); destination(d_kern); };
log { source(s_sys); filter(f_default); destination(d_mesg); };
log { source(s_sys); filter(f_auth); destination(d_auth); };
log { source(s_sys); filter(f_mail); destination(d_mail); };
log { source(s_sys); filter(f_emergency); destination(d_mlal); };
log { source(s_sys); filter(f_news); destination(d_spol); };
log { source(s_sys); filter(f_boot); destination(d_boot); };
log { source(s_sys); filter(f_cron); destination(d_cron); };


### リモートsyslog集約用設定
source s_remote { udp(ip(0.0.0.0) port(514)); };
destination d_remote { file("/var/log/remote/$HOST/$HOST.$YEAR$MONTH$DAY"); };
log { source(s_remote); destination(d_remote); };

syslog-ngの『source、destination、filterを定義→logでくっつける』書式のほうが、

  • 一貫性があって
  • 設定の境目やつながりがわかりやすい

と思うのですがどうでしょうか。