2006年04月27日

サーバ管理者向け無精のすすめ 〜ちょっと便利なツールの紹介〜

はてなブックマークに登録

弊社のLinuxサーバ、ネットワークインフラのDSASの特徴のひとつに、100台近くある全てのサーバの内容が(数個の役割設定ファイルを除いて)同期されているという点があります。

これにより、

スケーラビリティ
予備機をサービス投入するだけで済むので、テレビCMなど突発的な高アクセス時にも迅速な対応が可能です。
増強が容易
サーバをラックマウントしたら適当なサーバからまるまんまコピーすればクラスタに参加可能です。まとまった台数の増強をする際に、いちいちCD-ROMからOSをインストールしていると日が暮れちゃいます。
役割の変更が容易
ディスクの内容が同じなので、もし、メールサーバが故障しても、適当なWebサーバの役割設定ファイルを変更して再起動するだけでメールサーバに早変わりできます。
メンテナンスが容易
ディスク上のファイルを更新した場合は、rsyncなどで全サーバに同期コピーすれば更新完了です。「あのマシンはXXXコンテンツのXXXサーバなのでこのファイルをコピーしちゃだめだ」といった台数が増えた場合にありがちなメンテナンスコストの増加とそれに伴うオペレーションミスの軽減を実現しています。

などといったメリットが生まれます。

さて、ここでキモになるのはサーバ間でのファイル/ディレクトリの同一性で、

  • ファイルを変更する前に、念のため同一性の確認をする。
  • 異なっている場合は差分を確認する。
  • ファイルを更新したら確実に全サーバにコピーする。
  • コピーが終わったら全サーバで同一であるか確認する。

という点に注意せねばなりません。

これらをやろうとすると意外に煩雑なので、DSASでは作業を補助するシェル関数やaliasを作り、できるだけ作業の簡素化とオペレーションミスを防ぐようにしています。

というわけで、今回はファイル更新の流れに沿って、これらの小物ツールの紹介をしたいと思います。

同一性の確認



まずはファイルを変更する前に同一性の確認をします。

$ DIFFALL /var/tmp/editme
/var/tmp/editme
LOCAL 342dbfdd15958fcbaf518c4ed438a870
w101 342dbfdd15958fcbaf518c4ed438a870
w102 342dbfdd15958fcbaf518c4ed438a870
w103 433c576bc70d63869f21ad81adee2ccd


DIFFALLは、全サーバについて、引数で指定されたファイルのMD5を表示します。これにより、

  1. ファイル内容が異なるサーバがあるかどうか。
  2. 異なるファイルがある場合、同じファイルを持つサーバはどれとどれなのか。

ということが容易に確認できます。

より効果的に確認するには、1の場合なら
DIFFALL /var/tmp/editme | grep -v "ローカルの/var/tmp/editmeのMD5"

2の場合なら
DIFFALL /var/tmp/editme | sort -k2

とするとよいでしょう。

DIFFALLのソースは他のもまとめて末尾に掲載します。

DIFFALLの中で実行しているget_serversはサーバ名を配列で返すものだと思ってください。(実際のDSASではもっと凝った作りでサーバ名の一覧を動的生成しています)

異なるファイルの差分の確認



もし、内容の異なるファイルがあった場合、どのように確認すればよいのでしょうか。

単純な方法だと、リモートのファイルをローカルのにコピーしてきて、ローカルのファイルとdiffを取るのが考えられますが、異なるファイルが多いと面倒です。

そこでリモートのファイルとdiffが取れるrdiffというシェル関数を作りました。

rdiff [diff option] [-R] FILES
FILES := file file
file host:file


こんな風に使います。

$ rdiff -u /var/tmp/editme w103:/var/tmp/editme
--- /var/tmp/editme 2006-04-27 21:47:18.000000000 +0900
+++ /dev/fd/63 2006-04-27 21:51:55.212130926 +0900
@@ -1 +1,3 @@
onajikana?
+kokoga chigauYO!


ここで、「同じパスを2度指定するのは面倒くさいなぁ」と思ったアナタ。スルドイです。(^_^

rdiffはdiffコマンドの全てのオプションに加え、パスの相対指定のための-Rオプションを追加してあります。DSASの場合、ディレクトリ構成も一緒なので、-Rオプションを使えばこんな風にファイルパスを省略することができます。

rdiff -uR /var/tmp/editme w103:/
rdiff -uR /very/very/looooooooooooooooooong/path w103:/


全サーバにファイルをコピーする



編集が終わったら全サーバにコピーをします。

$ get_servers
$ for i in ${SERVERS[@]}; do
> scp -p /var/tmp/editme $i:/var/tmp/editme
> done

とすれば全サーバにコピーできますが、タイプ数が多くて滅入るので、SYNCALLというシェル関数を使っています。

$ SYNCALL /var/tmp/editme 
[w101]

[w102]

[w103]
editme


だいぶタイプ数が減りました。(^_^

また、-nオプションで実際のファイルコピーを行わずdry runすることもできます。

さて、ファイルのコピーではなくても、「全サーバでXXXする」という作業はわりとよく発生します。その度に for i in ... とタイプするのは面倒なので、FOAALL というaliasを定義しています。

例えば、全サーバのロードアベレージを見る場合、FORALLを使えばこんな風に簡単になります。
$ FORALL echo -n "$i "; ssh $i cat /proc/loadavg ; done
w101 1.45 0.66 0.58 1/221 4430
w102 2.43 0.94 0.69 1/397 18833
w103 0.00 0.01 0.00 1/248 6833


まとめ



今回はオペレーションの手間を減らせるようなヘルパーアプリケーション的なシェル関数やaliasを紹介しました。

手間が減りタイプ数が減れば、その分、打ち間違いなどのオペレーションミスの低下の効果も期待できます。

みなさんもいつも何気なく打っているコマンドをもっと簡素化できないか見直してみてはいかがでしょうか? そしてステキなものができたら是非、トラックバックして教えてください。

(ひ)

ソースコード



get_servers() {
SERVERS=('w101' 'w102' 'w103')
}

DIFFALL() {
get_servers
for f in "$@"; do
echo $f
if [ ! -f $f ]; then
echo "no such file: $f"
continue
fi
digest_l=$(md5sum $f | awk '{print $1}')
printf '%-6s' 'LOCAL'
echo $digest_l
for h in ${SERVERS[@]}; do
printf '%-6s' $h
digest=$(ssh $h md5sum $f | awk '{print $1}')
echo $digest
done
done
}

rdiff() {
diff_orig=$(which diff 2>/dev/null)
if [ -z "$diff_orig" ]; then
echo "cannot find diff command." 1>&2
return 1
fi

declare -a opts
declare -a files

optv=("$@")
optc=${#optv[@]}

files=(${optv[$optc-2]} ${optv[$optc-1]})
unset optv[$optc-1]
unset optv[$optc-2]

file_relative=
i=0
for o in "${optv[@]}"; do
case "$o" in
*R*)
optv[$i]=$(echo $o | sed -e 's/\-\?R//g')
file_relative=${files[0]}
;;
esac
i=$(($i+1))
done

case "${files[@]}" in
*:*)
for i in 0 1; do
case ${files[$i]} in
*:*)
eval $(echo ${files[$i]} | awk -F':' '{printf "t_host=%s t_file1=%s", $1, $2}')
if [ -n "$file_relative" ]; then
t_file1="$file_relative"
fi

files[$i]=<(ssh $t_host cat $t_file1)
;;
*)
;;
esac
done
;;
*)
;;
esac

$diff_orig "${optv[@]}" "${files[@]}"
}

SYNCALL() {
get_servers
syncopt=
case $1 in
-n)
syncopt=-n
shift
;;
esac
if [ $# -le 0 ]; then
echo 'missing filename'
return
fi
for f in "$@"; do
if [ ! -f $f ]; then
echo "no such file: $f"
continue
fi
for h in ${SERVERS[@]}; do
echo "[$h]"
rsync -lptgoDuv $syncopt $f $h:$f | egrep -v '^(building file list|(wrote|sent) [0-9]+ bytes|total size is )'
done
done
}

alias FORALL='for i in ${SERVERS[@]}; do '



  • 2007-04-19: rdiffのコードを更新

klab_gijutsu2 at 22:33│Comments(0)TrackBack(2)

トラックバックURL

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

1. [linux] 便利Tips集を集めてみました  [ moto0215の日記 ]   2007年01月10日 22:17
サーバ管理系 複数の同系等のサーバに一括でコマンドなどを発行する技 DSASさんからのネタで、管理中のサーバ群に同期をとったり、ファイルを配布したりと、かなり重宝するネタです。 これを使用するには、あらかじめ、SSHのパスフレーズなしの鍵とかでサーバ間をイケイケに
2. 管理工数の低下によるコスト削減  [ Tadahira's blog ]   2007年02月11日 00:58
DSASさんのところにとても便利な記事を発見。 複数の同一構成のサーバのファイル...

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント