2013年04月16日

ClusterShellで始めるSSHで並列・分散サーバー管理

並列大好き。並列と言われたら評価しない訳にはいかない。今日はSSHで管理対象ホストに対してコマンドを並列実行出来るClusterShellの使い方・特徴をご紹介します。

cluster shell



ClusterShell の概要


ClusterShellはPythonで記述された並列、スケーラビリティを特徴としたSSHによるサーバー管理、コマンド実行ツールで、各種OS(Red Hat/CentOS, Debian/Ubntu, Fedora, Mac OS)向けにパッケージが用意されており、PythonアプリケーションでAPIとしても利用出来ます。

clusershell - overview


ClusterShell のインストールと設定


ClusterShellは各ディストリビューションのパッケージ管理システム(apt, yumなど)からもバイナリ版をインストール出来ますが、最新版では無く不具合(※1)が含まれているケースもあることから、ソースからビルドする又はGitHubのプロジェクト・ホームから最新のバイナリ版をダウンロードしてインストールすることをお勧めします。

※1 Ubuntuでは$ sudo apt-get install clustershellで1.5.1が利用できますが、/etc/clustershell配下にgroupsファイルが無いため、デフォルトでは幾つかのオプションでエラーが発生します。

download - clustershell from github


こちらはダウンロードしたtarボールからビルドする手順。

$ tar xvzf clustershell-1.6.tar.gz 
$ cd clustershell-1.6
$ sudo python setup.py install
$ sudo cp -r conf /etc/clustershell

続いて、管理対象ホストにClusterShellをインストールした自ホスト(マネージャー)のSSH公開鍵をコピーします。

ここではopenssh-client(s)パッケージに含まれるssh-copy-idコマンドを使って管理対象ホスト(172.16.0.115)にsshkey-genコマンドで作成した自身の公開鍵(パスフレーズ無し)をコピーしています。

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/netbuffalo/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/netbuffalo/.ssh/id_rsa.
Your public key has been saved in /home/netbuffalo/.ssh/id_rsa.pub.
The key fingerprint is:
49:c3:cc:71:e8:f1:f4:aa:9c:67:fc:da:d0:xe:01:33 netbuffalo@localhost.localdomain
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|       .         |
|        +        |
|       o o       |
|      E S        |
|     o = +       |
|    = o * =      |
|   . * o B .     |
|    . o.o +o.    |
+-----------------+

$ ssh-copy-id netbuffalo@172.16.0.115
Warning: Permanently added '172.16.0.115' (RSA) to the list of known hosts.
netbuffalo@172.16.0.115's password: 
Now try logging into the machine, with "ssh 'netbuffalo@172.16.0.115'", and check in:

  .ssh/authorized_keys

to make sure we haven't added extra keys that you weren't expecting.

これで準備はおしまい。 全ての管理対象ホストに公開鍵を登録しましょう(ssh-copy-idが無い場合は「pssh, pdshでもない! TakTukを使って複数ホストで並列・同時コマンド実行・ファイル転送する」を参考に手動登録)。

より詳細な設定(標準で利用するSSHユーザーIDの変更、タイムアウトなど)を行う場合には /etc/clustershell/clush.conf ファイルで指定出来ます。


ClusterShell の基本的な使い方


並列コマンド実行はcrushコマンドを使って行います。 次の例では-wオプションでホストを指定し、各ホスト上でuname -rを並列実行しています。
もし、別のユーザーでログインする場合には-lオプションでユーザーIDを指定しましょう。

$ clush -w 172.16.0.115,172.16.0.116,172.16.0.198 uname -r
172.16.0.116: 2.6.9-89.29.1.ELsmp
172.16.0.198: 2.6.18-194.17.4.el5
172.16.0.115: 2.6.9-89.29.1.ELsmp

これは次のように記述することも出来ます。start - end で範囲指定し、かつ範囲外の172.16.0.198も追加しています。

$ clush -w 172.16.0.[115-116,198] uname -r
172.16.0.115: 2.6.9-89.29.1.ELsmp
172.16.0.198: 2.6.18-194.17.4.el5
172.16.0.116: 2.6.9-89.29.1.ELsmp

他にも正規表現に近いホスト記法がありますが、これらの記法を利用する場合、nodesetコマンドを使って事前に書式チェックすることも出来ます。

$ nodeset -e target-host[1-5] -x target-host3
target-host1 target-host2 target-host4 target-host5

この例ではホスト名としてtarget-host1~10を範囲指定し、その中からtarget-host3だけを-xオプションで除外(exclude)していま
す。


続いて出力結果のグルーピング。-bオプションを付けてuname -rを実行してみます。

$ clush -w 172.16.0.[115-116,198] -b uname -r
---------------
172.16.0.115,172.16.0.116 (2)
---------------
2.6.9-89.29.1.ELsmp
---------------
172.16.0.198
---------------
2.6.18-194.17.4.el5

2つのホストで 2.6.9-89.29.1.ELsmp、172.16.0.198だけが 2.6.18-194.17.4.el5 であることが分かりやすく確認出来ます。
複数ホストのミドルウェア、フレームワークのバージョンを確認するときには嬉しいですねえ。


もちろん、対話形式もサポート。

$ clush -b -w 172.16.0.[115-116]
Enter 'quit' to leave this interactive mode
Working with nodes: 172.16.0.[115-116]
clush> 
clush> uptime
---------------
172.16.0.115
---------------
 18:35:35 up 119 days,  6:58,  0 users,  load average: 0.03, 0.05, 0.02
---------------
172.16.0.116
---------------
 18:35:35 up 119 days,  6:52,  2 users,  load average: 0.11, 0.08, 0.02


ちなみに、テキストだけでは伝わりませんがコマンドの異常終了・SSH接続失敗と正常終了をカラリゼーションして区別してくれます。

clusershell - stdout colorlization


ClusterShell 応用編 - グループ機能、ファイルの送受信


Clusterシェルはホストのグループ化も出来ます。 nodeset -lと入力して表示されるのがグループの一覧。

$ nodeset -l
@adm
@oss
@mds
@io
@compute
@gpu
@all

グループの定義は /etc/clustershell/groups ファイルで行います。web、dbという名前でグループを追加するならこんなふうになります(ここでは常にIPアドレスで記述していますがホスト名でも構いません。また先程説明した範囲表記も可能です)。

$ sudo vi /etc/clustershell/groups
adm: example0
oss: example4 example5
--- snip ---
db: 172.16.0.115 172.16.0.116
web: 172.16.0.198

ファイルを保存後、nodesetでdbグループに含まれるホストを表示してみましょう。

$ nodeset -e @db
172.16.0.115 172.16.0.116

clushでグループを指定するには-gオプションを利用します(@マークは付かないことに注意)。

$ clush -g db /etc/init.d/mysqld stop

これまでに説明した-x(exclude)オプションを組み合わせれば、グループから一部のホストを除外することもできますよ。


最後はファイルの送受信。 送信は--copyオプションでローカル、リモートのファイルを次のように指定します。

$ clush -v -w 172.16.0.[115-116] --copy /tmp/data.zip /tmp/data.zip
`/tmp/data.zip' -> 172.16.0.115,172.16.0.116:`/tmp'
`/tmp/data.zip' -> 172.16.0.115,172.16.0.116:`/tmp'

送信は良いとして受信はどうなるでしょうか? 同じファイル名だったらローカル側で上書きされちゃいそうですが。

$ clush -v -w 172.16.0.[115-116] --rcopy /tmp/data.zip --dest .
172.16.0.115,172.16.0.116:`/tmp/data.zip' -> `.'

$ ls
data.zip.172.16.0.115  data.zip.172.16.0.116

--rcopyでファイルの受信(getオペレーション)を行いますが、ローカルに保存されたファイル名にはホスト名が追加されるんですね。


clushはパイプで標準入力を受け取ることも出来るので、こんな事もできますよ。

$ wget -O - https://github.com/path/to/clustershell-1.6.tar.gz | clush -w 172.31.0.12 tar -C /tmp -xzf -
Resolving github.com (github.com)...
--- snip ---
HTTP request sent, awaiting response... 200 OK
Length: 194227 (190K) [application/x-tar]
Saving to: `STDOUT'

100%[=============================>] 194,227     --.-K/s   in 0.06s   
2013-04-16 15:33:19 (3.15 MB/s) - written to stdout [194227/194227]

$ clush -w 172.31.0.12 ls -d /tmp/cluster*
172.31.0.12: /tmp/clustershell-1.6

wgetでダウンロードしたファイルをパイプでclushに渡し、そのままリモート・ホスト上で解凍しています。

clushの魅力が伝わったでしょうか。


ClusterShell をAPIとして利用する。


ClusterShellはPythonライブラリとして利用することも可能です。例えば複数ホストの構成管理だけでは無く、ステータス監視、アラーム通知に使うなんてどうでしょうか。

次のコードはAPIを利用して指定したノード(管理ホスト)のロードアベレージを取得、直近1分平均が3.0より大きければ、アラーム・メッセージを出力する簡単なサンプルです。

# coding: utf-8
from ClusterShell.Task import task_self

task = task_self()

# monit load average
task.shell ("cat /proc/loadavg", nodes="172.16.0.[115-116,198]")

task.resume() # start scalable ssh command execution

for buf,nodes in task.iter_buffers():
  # /proc/loadavg, e.g. -> 0.48 0.44 0.38 1/602 10015
  ary = str(buf).split(' ')
  lavg1min = float(ary[0])
  if lavg1min > 3: # 1min loadavg over 3.0 ?
    for node in nodes: # same output nodes
      print "HIGH LOAD! - node: %s, loadavg(1min): %s" % (node, buf)

このコードをmonit.pyとして保存し、実行すると次のような出力になります。

$ python monit.py 
HIGH LOAD! - node: 172.16.0.198, loadavg(1min): 3.08 2.02 1.01 1/142 16948
HIGH LOAD! - node: 172.16.0.116, loadavg(1min): 3.17 2.04 1.00 1/91 11824

詳しいAPIドキュメントはこちら


それでは、より良い並列管理ライフを!



[24時間365日] サーバ/インフラを支える技術 ‾スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)
安井 真伸 横川 和哉 ひろせ まさあき 伊藤 直也 田中 慎司 勝見 祐己
技術評論社
売り上げランキング: 6,513

Posted by netbuffalo at 21:30│Comments(0)TrackBack(0)Linux | ユーティリティ


この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/netbuffalo/4436515

コメントする

名前
URL
 
  絵文字