ohaiを使ってサーバの情報をプログラムで扱おう

斎藤です。こんにちは。

今日は、Chefをインストールすると共に入るohaiを用いて、サーバの情報をプログラムで扱ってみます。

※ohaiは6.16(github版)を用いています

ohaiとは

もともと、Chef Client/Chef Soloが実行環境の情報を取得するためのライブラリです。ただ、単独でもライブラリを使用したり、コマンドを実行する事で、Chefと同様に情報を利用する事ができます。ポイントは、外部コマンドの結果を内部でパースしてプログラムで扱いやすい形にできます。まさに、パーサーライブラリですね。

ohaiがなければ、コマンドでサーバの環境(例えば"df")の情報を取得しようとする時、コマンドの結果を取得した後に「お手製」のパーサーでプログラム内で活用できるようにしなければなりません。これが、"df"のほうに簡単なものならまだしも、"ip addr show"のような設定状況によっては複雑になるものですと、なかなか厄介です。

なお、ohaiはRubyのライブラリですので、Rubyからはrequireすればすぐに利用できます。Ruby以外のプログラムから利用する場合は、"ohai"コマンドの実行時に出力されるJSON形式の標準出力を受け取り、プログラム内でハッシュ変数として利用すればOKです。

ohaiで取得できる情報

どんな情報があるのか...?

少しohaiを触った事がある方なら、いろいろな情報を取得できることはご存知かと思います。ただ、どんな情報が取得できるのかの詳細は、Chef公式ドキュメントには明記されていません。せっかくですので、調べてみました。

執筆時点での最新版である6.16は、特に"lib"ディレクトリ内が次のような構造になっています。

lib/
└── ohai
    ├── mixin
    └── plugins
        ├── aix
        ├── darwin
        ├── freebsd
        ├── hpux
        ├── linux
        ├── netbsd
        ├── openbsd
        ├── sigar
        ├── solaris2
        └── windows

"ohai"ディレクトリは、ohai自体の動作を司る設定やコードが収められています。

"mixin"ディレクトリは、主に後述するプラグインを実行するためのコードが収められています。

"plugins"ディレクトリは、情報を取得するためのコードが収められています。ここが、要ですね。"plugins"直下には、プラットフォームに左右されない情報の取得のためのプラグインがあります。主に、プログラミング言語の情報を取得しています。さらに、サブディレクトリにプラットフォーム毎のプラグインが収められており、主にハードウェアやOSの設定情報を取得しています。詳細は、種類が多岐に渡るため、次の節で説明します。

取得できる情報

ホスト情報

ホスト上の情報、特にハードウェア等の低レイヤーの情報を取得します。パフォーマンスが気になったときにまず最初に見る項目が押さえられています。

  • CPU (ベンダー名, モデル名, ID, サポートしている命令)
  • メモリ
  • ブロックデバイス
  • ディスク (パーティション, マウントポイント, 使用量, 空き容量, 全容量)
  • DMIの情報

特に、ディスクパーティションがどう切られているかを手軽に知る事ができる点は素晴らしいですね。

OS情報

OSに設定されている情報を取得します。取得できる項目に細かい違いはありますが、基本としてohai側でOSの違いを吸収できるので、プログラム上で取り扱いやすくなっています。

  • uptime
  • ホスト名
  • プラットフォーム名 (プログラム内でOS別分岐をさせやすい名称) とバージョン
  • OS種別
  • OSバージョン (特にカーネル)
  • ネットワーク (NIC, Link Speed, MAC Address, TCP Version, MTU, IP Address)
  • ネットワークカウンタ (送信済み・受信済み・ドロップ・エラー)
  • ルーティング (ゲートウェイ, デフォルトゲートウェイ)
  • 読み込んでいるカーネルモジュール
  • 稼働中のプロセス一覧
  • 登録ユーザ (ホームディレクトリ, uid, gid, Shell, コメント)
  • 登録グループ (gid, 所属ユーザ)

ネットワーク周りは、特に自力で"ip addr show"のパーサーを書くと、複数のNICやIPを解釈するときにちょっと苦労するポイントですが、ohaiなららくちんです。

プログラミング言語

インストールしているプログラミング言語の開発・実行環境についての情報、主にバージョンを収集します。収集にあたっては、コンパイラ・インタプリタにパスが通っている事が前提となります。対象は次の言語です。

  • C (gcc, glibc, 各プラットフォームのコンパイラ)
  • Erlang
  • groovy
  • Java
  • Lua
  • Mono
  • node.js
  • Perl
  • PHP
  • Python
  • Ruby (環境変数を含めて他の言語より綿密に収集できます)

「これは調べておきたい」というものから、「これもサポートしているんだ」というものもあったのではないでしょうか。

仮想環境設定情報

AWSなどのIaaSサービス上で稼働しているサーバ、及びOpenStackなどの仮想環境で動いているサーバにおける、基盤上で設定された情報を取得します。

  • AWS (EC2 Metadata)
  • rackspace
  • linode
  • eucalyptus
  • OpenStack
  • VMware Toolsなどの仮想化時に利用するソフトウェアの状態

プラットフォーム固有の情報

プラットフォーム(OS)固有で取得できる情報です。

  • LSB (Linuxのみ)
  • ZFSの状態 (Solarisのみ)
  • PnPデバイスの状態 (Windowsのみ)

ohaiを使って情報を取得してみる

インストール

Ruby以外からohaiを利用する場合は、Chef ClientがインストールされていればOKです。インストールしていない方は、オムニバスインストーラを使ってインストールすると簡単です。公式ドキュメントの次のページを参照してください。

Rubyからライブラリとして利用する場合で、Chefをgemコマンドからインストールしていない場合、gemコマンドを用いてインストールしてください。

# gem install ohai

コマンドライン

ohaiはコマンドを実行すると、収集した情報をJSON形式で出力します。動作確認をかねて、コマンドを実行してみて下さい。

# ohai
{
  "languages": {
    "ruby": {
      "platform": "x86_64-linux",
:
(中略)
:
    "vmalloc_total": "34359738367kB",
    "vmalloc_used": "13136kB",
    "vmalloc_chunk": "34359717460kB"
  }
}

Chef

"node"変数(※1)に収まっていますので、こちらを利用します。

コミュニティのCookbookでもよく用いられていますが、特にrecipe内でOSやプラットフォーム別に分けて処理したいときに便利です。

case node[ :platform_family ]
when "rhel"
    〜処理を書く〜
when "debian"
    〜処理を書く〜
when "ubuntu"
    〜処理を書く〜
else
end

Ruby

requireしますと、ライブラリとしてohaiを扱う事ができます。

require 'ohai'
oh = Ohai::System.new
oh.all_plugins  # 全ての情報を取得
p oh.data  # 変数の内容をdump

例えば、RAMの使用率を調べるには次の通りにします。

require 'ohai'

def getValue( str_value )
    return str_value.sub( "kB", "" ).to_f
end

oh = Ohai::System.new
oh.require_plugin( "os" )
oh.require_plugin( "#{oh[:os]}/memory" )
percent = getValue( oh[ :memory ][ :free ] ) / getValue( oh[ :memory ][ :total ] ) * 100.0
print "Mem Free: %.2f%%\n" % percent

実行すると次のような結果が出力できます。ソースコードのファイル名を"ohai_test.rb"とします。

$ ruby ohai_test.rb
Mem Free: 20.63%

※注: linuxのメモリ使用量は"/proc/meminfo"由来の値が取得できます。

OS別のプラグインを利用する場合、OSプラグインを通じて取得したOS名を用いて読み込むと、容易に利用できます。

それ以外 - Pythonなど

ohaiをコマンドを通じて実行し、JSONのパーサーに読み込ませた後に利用します。先ほどのRubyのプログラムと同じ処理を行うものは、次の通りに書く事ができます。

from subprocess import Popen, PIPE
import json

def getValue( str_value ):
    return float( str_value.replace( "kB", "" ) )

ret = Popen( "ohai -l error", stdout=PIPE, shell=True )
ret_data = ret.communicate()[0]
if ret.returncode != 0:
    print "Exec failed."
    exit(1)
oh  = json.loads( ret_data )
percent = getValue( oh[ "memory" ][ "free" ] ) / getValue( oh[ "memory" ][ "total" ] ) * 100.0
print "Mem Free: %.2f" % percent

※注: 環境によってはINFOレベルのログが出るため、上記の例ではログ出力レベルをerrorまで引き上げています。

実行すると次のような結果が出力できます。ソースコードのファイル名を"ohai_test.py"とします。

$ python ohai_test.py
Mem Free: 20.63%

例示していませんが、標準出力を受け取りJSONのパースができれば、どの言語でも取り扱う事ができます。

まとめ

ここまで、ohaiがサーバのどのような情報を取得でき、そしてどのようにプログラムで扱うかをお話ししました。Chefまたはgemを入れる手間こそありますが、自力でパーサーを書くよりも手軽かつより確実にサーバの情報を取得できます。また、運用時に利用するプログラムで活用する事もできます。

また、ohaiのプラグインを自分で書き、より多くの情報を取得する事もできます。例えば、ご自身の環境で利用しているミドルウェアのバージョンを取得するため等のプラグインを書くことができます。詳細は、参考文献をご覧ください。

サーバのインベントリ情報管理の自動化などで、Chefとともに利用してみてください。

それでは、ごきげんよう。

※1: attributeを書くとき、ohaiの変数名とぶつからないように気をつける必要があります。Community Cookbookで実践されている、名前空間をレシピ毎に分けるのが良いかと思います。

参考文献