右往左往ブログ

日々よりみち

pgrep, pkillでプロセスをkillする

特定のプロセスをkillするときは、いつも以下のようなコマンドを使っていました。

# ps -ef | grep java | grep -v grep
root     12927     1  2 15:51 pts/0    00:00:05 java -Xmx64m -jar start.jar
# kill 12927

もっといい方法はないのかと思っていたら、pgreppkillという方法があるのを知りました。
pgrepは、プロセス名で絞り込んだPIDを表示させることができます。

# pgrep java
12927

-lオプションの場合、コマンドが表示されます。

# pgrep -l java
12927 java

ただ、複数のプロセスがある場合は、区別がつきません。 この場合は、-fオプションにより詳細が表示されます。

# pgrep -lf java
12927 java -Xmx64m -jar start.jar

同様に、プロセスをkillする場合もpkillにより、いちいちPIDを調べる必要がなくなります。 オプションも同様で、-fオプションによりプロセスの引数も含めて指定することができます。

# pkill -f "java.*start\.jar"

これは便利。

ZABBIXのホスト自動登録機能でJMXの設定を行う

ZABBIXのホストの自動登録機能の問題点

Zabbixでのホストの自動登録機能は、Zabbixエージェントを導入したノードに、自動的にホストグループの組み込みやテンプレートのリンクを行うできる便利な機能です。これにより、はじめてZabbixエージェントを導入したノードに対しても、監視の初期設定を自動的に行うことができます。

が、通常ではJMXインターフェースは自動で認識されないという欠点があります。そのため、JMX関係の設定はホストの認識後に手動で設定する必要があります。また、それによってJMXを利用するテンプレートをリンクすることもできません。

ZABBIX APIを利用したカスタムスクリプト

そこで、ホストの自動登録機能を生かしつつJMX関係の設定(ホストへのJMXインターフェースの追加、JMX関係テンプレートのリンク)を行うために、ZABBIX APIを利用したカスタムスクリプトを自動登録機能に組み込みました。これにより、ホストはJMXインターフェースの追加やJMX関連テンプレートのリンクも自動で行うことができるようになっています。
zabbix-jmxsetting

なお、テンプレートのリンク用に、以下を利用しています。
Zabbix 2.0 Tomcat 7 JMX Template

設定

スクリプトの配置

上記から取得したzabbix-jmxsetting.shをZABBIXサーバ上の好きな場所に置きます。
今回は/usr/lib/zabbix/settingscripts配下に置きます。chmodで実行権を与えてください。

ホストの自動登録

まずは、通常通り、ZABBIXのホストの自動登録機能を利用します。
f:id:unchemist:20131103011738p:plain

カスタムスクリプトの設定

[アクションの実行内容]から、リモートコマンドを追加してください。

  • 実行内容のタイプ : リモートコマンド
  • ターゲットリスト : 現在のホスト
  • タイプ : カスタムスクリプト
  • 次で実行 : Zabbixサーバー
  • コマンド : /usr/lib/settingscripts/zabbix-jmxsetting.sh {HOST.HOST} {HOST.IP}

スクリプトの場所は、置いた場所に応じて適宜読み替えてください。

f:id:unchemist:20131103011746p:plain

実行結果

f:id:unchemist:20131103011750p:plain

新しいホストが登録されてしばらく待つと、上記の通りTemplate JMX Tomcat 7が追加され、JMXのアイコンも点灯していることが分かります。最初は登録されてなくて焦りますが、しばらく待って画面をリロードすると、遅れて点灯するはずです。

f:id:unchemist:20131103011754p:plain

ホストの詳細を見ても、JMXインターフェースが設定されていることが分かります。

注意事項

スクリプト内で、Template JMX Tomcat 7に決め打ちにしているので、あんまり汎用的ではないです。また、ZABBIX APIを利用する関係で、スクリプト内のユーザ名/パスワードをそれぞれの環境用に書き換える必要があります。

あと、ZabbixエージェントのインターフェースのIPとJMXインターフェースのIPが同じである場合でしか適用できないので、もしインターフェースが異なる場合は対応できません。この辺りは反省…。

あと、JSON形式の結果をパースするためにjqを利用しています。そのため、jqがZABBIXサーバにインストールされている必要があります。 jq


内部的な話

スクリプトの中身は、単にZABBIX APIを利用してJMXインターフェースの追加やテンプレートのリンクを行っているだけです。
結果はJSON形式なので、これをjqコマンドでパースして必要な値だけ取得し、加工しています。

ZABBIX APIを始めて利用して、最初はかなり戸惑いましたが、慣れるとかなり便利でした。APIを使っていろいろできそうです。
あとjqも物凄く便利です。JSON形式で返すZABBIX APIとはかなり相性がいいですね。

curlコマンドでプログレスメータを表示させない

curlコマンドをパイプでリダイレクトすると、大体以下のような標準出力になります。

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0    68    0    68    0   163   1893   4538 --:--:-- --:--:-- --:--:--     0

普段は問題ないのですが、表示を消したい場合はどうするんだろうと思ったら、-sオプションで行けました。

curl -s ${URL} | ${COMMAND}

というか上記を「プログレスメータ」ということらすら知りませんでした…。

chefでserviceを正しく管理する(kibana)

前々回、kibanaのサービスとしてkibana-daemon.rb(を中で使っている/etc/init.d/kibana)を使っていました。

chefで、最初にkibanaをインストールしてサービスを起動しようとしたときに、 サービスがなぜかスキップされて起動しません。recipeではちゃんと指定しています。

site-cookbooks/kibana/recipes/default.rb :

cookbook_file "/etc/init.d/kibana" do
  source "kibana"
  action :create_if_missing
  mode 00755
end

bash "add_kibana_service" do
  code <<-EOL
    chkconfig --add kibana
  EOL
end

service "kibana" do
  action [:start, :enable]
end

出力ログ :

Recipe: kibana::default
〜中略〜〜
  * service[kibana] action start (up to date)
  * service[kibana] action enable (up to date)

実際に確認してみても、確かに起動していません。

$ service kibana status
kibana: no instances running

結論から言うと、戻り値が管理されていませんでした。
chefでserviceリソースを利用するときは、service <service_name> statusの戻り値を適切に管理する必要があるようです。
サービス停止状態のときは"3"を返すことで、chefは適切にサービスを管理してくれます。
というか、kibana-daemon.rbに任せる必要すらない気がするので、statusコマンドに任せることにしました。

/etc/init.d/kibana (元々は /usr/share/kibana/sample/kibana) 変更前:

KIBANA_PATH="/usr/share/kibana"
# 略
 status)
    ruby $KIBANA_PATH/kibana-daemon.rb $1
  ;;

変更後 :

KIBANA_PATH="/usr/share/kibana"
. /etc/init.d/functions
# 略
 status)
    # ruby $KIBANA_PATH/kibana-daemon.rb $1
    status -p ${KIBANA_PATH}/tmp/kibana.pid kibana
  ;;

これで動作するようになりました。

$ knife solo cook <server_name> 

出力ログ :

Recipe: kibana::default
〜中略〜〜
  * service[kibana] action start
    - start service service[kibana]

  * service[kibana] action enable (up to date)

Rubyを知らずにchefでrecipeを書くときのTips

Rubyをほとんど使ったことがなくて、そのままchefのrecipeを書こうとすると、いろいろと細かいところがどう書くのか分からなくなります。 ということで、個人的に困ったところをメモします。

複数のリソースを使う

%w{... ...}で必要な分だけ記入して、eachでぐるぐる回します。

%w{vim-enhanced yum-utils}.each do |pkgs|
  package pkgs do
    action :install
  end
end

環境変数を設定する

ruby_blockの中でENV["ENV_NAME"]を使います。

ruby_block  "set-env-java-home" do
  block do
    ENV["JAVA_HOME"] = "/usr/java/default"
  end
end

恒久的に設定する場合は、/etc/profile.d/配下にスクリプトを置きます。

file "/etc/profile.d/jdk.sh" do
  content <<-EOS
    export JAVA_HOME="/usr/java/default"
  EOS
  mode 0755
end

ファイルが存在するかどうかを判定する

File.exist?("<PATH>")を使います。
not_ifと併用すると、ファイルが存在しない場合に実行されます。

bash "fluentd_repo" do
  code <<-EOL
   curl -L http://toolbelt.treasure-data.com/sh/install-redhat.sh | sh
  EOL
  not_if {File.exist?("/usr/sbin/td-agent")}
end

特定の文字列が含むかどうかを判定する

環境変数が設定されているかどうかを判定したくて調べました。
<STRING>.include?(<CHECK_STRING>)で利用します。

ruby_block  "set-env-rbenv" do
  block do
    ENV["RBENV_ROOT"] = "/usr/local/rbenv"
    ENV["PATH"] = ENV["RBENV_ROOT"] + "/bin:" + ENV["PATH"]
  end
  not_if {ENV["PATH"].include?("/usr/local/rbenv")}
end

上記では、環境変数${PATH}"/usr/local/rbenv"が含まれていない場合に環境変数の設定が行われます。

複数の(可変の)行を挿入する

/etc/hostsをtemplateとしてhosts.erbで管理するとき、attributeでホストの一覧を直接記述してしまうと、ホストが増えた時に、いちいちtemplateごと変えることになり面倒…なので、hashで設定することにします。

attributes/default.rb :

default['Network']['config']['HostIPList'] = {
    "host1" => "192.168.5.1",
    "host2" => "192.168.5.2",
    "host3" => "192.168.5.3",
    "host4" => "192.168.5.4"
}

templates/default/hosts.erb :

<% node['Network']['config']['HostIPList'].each do | hostname , ip | %>
<%= ip %>    <%= hostname %>
<% end %>

chefで上記templateを配布した後は、最終的に以下のようになります。

/etc/hosts :

192.168.5.1    host1
192.168.5.2    host2
192.168.5.3    host3
192.168.5.4    host4

もしホストを追加したくなったら、attributeに行を追加すればよさそうです。

Kibanaでkibana-daemon.rbを実行する時にはまった話

もともとはkibanaインストールディレクトリ内に、sample/kibanaが存在していて、これを/etc/init.d/向けに使おうとしたときにはまった話。

試しに実行してみる。

$ cd sample
$ ./kibana start
$

ところが、何も起こらない。

$ ps -ef | grep kibana
$

特に何も表示されない。内部ではkibana-daemon.rbを実行しているみたい。
仕方ないので色々調べてみましたが、そもそもrubyを知らないので、解析するのにえらく時間がかかりました…。

ruby kibana-daemon.rb start fails siliently

これによると、-tオプションで詳細出力することができるみたいです。

$ cd ../
$ ruby kibana-daemon.rb -t

以下出力結果:

/usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/pidfile.rb:94:in `initialize': No such file or directory - /usr/local/Kibana/tmp/kibana.pid (Errno::ENOENT)
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/pidfile.rb:94:in `open'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/pidfile.rb:94:in `pid='
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/application.rb:211:in `block in start_proc'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/application.rb:264:in `call'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/application.rb:264:in `start_proc'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/application.rb:296:in `start'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/controller.rb:70:in `run'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons.rb:197:in `block in run_proc'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/cmdline.rb:109:in `call'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/cmdline.rb:109:in `catch_exceptions'
    from /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons.rb:196:in `run_proc'
    from kibana-daemon.rb:25:in `<main>'

/usr/local/Kibana/tmp/kibana.pidファイルを作ることができないらしい。というかtmpディレクトリがない…。ということで作成。

$ mkdir sample/tmp

これで起動できるようになりました。

$ cd sample
$ ./kibana start
pid-file for killed process 4417 found (/usr/share/kibana/tmp/kibana.pid), deleting.

あとは、このファイルを/etc/init.d/配下に置いておけばいいかな。


以下余談:
最初はrubyデバッグ出力をしてみていましたが、よく分かりませんでした。

$ ruby -d kibana-daemon.rb
Exception `LoadError' at /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems.rb:1082 - cannot load such file -- rubygems/defaults/operating_system
Exception `LoadError' at /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems.rb:1091 - cannot load such file -- rubygems/defaults/ruby
Exception `LoadError' at /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45 - cannot load such file -- daemons
/usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/daemonize.rb:59: warning: assigned but unused variable - sess_id
/usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/daemonize.rb:102: warning: assigned but unused variable - pid
/usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/application.rb:337: warning: mismatched indentations at 'end' with 'def' at 326
/usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/application_group.rb:80: warning: assigned but unused variable - pid
Exception `ArgumentError' at /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/daemonize.rb:137 - The given fd is not accessible because RubyVM reserves it
Exception `ArgumentError' at /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/daemonize.rb:137 - The given fd is not accessible because RubyVM reserves it
Exception `ArgumentError' at /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/daemonize.rb:137 - The given fd is not accessible because RubyVM reserves it
Exception `ArgumentError' at /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/daemonize.rb:137 - The given fd is not accessible because RubyVM reserves it
Exception `Errno::EBADF' at /usr/local/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/daemons-1.1.9/lib/daemons/daemonize.rb:137 - Bad file descriptor

rubygems/defaults/operating_system」がないとかでエラーになるのは、特に問題ないらしいです。
Ruby 1.9.2とRubyGems 1.3.7とGem.pathの消失 - \ay diary