unoh.github.com

ベンチャー流サーバ構築のススメ(同期ツール編)

Sun Aug 06 08:40:32 -0700 2006

ダイエット中で炭水化物の量が気になる尾藤正人です。

前回のエントリベンチャー流サーバ構築のススメ(ソフトウェア編)では、主にOS周りのことについて書きました。複数台のサーバを管理するのに重要なのは極力構成を同じにすることです。そうすることで管理コストが大幅に下がります。

以前Klabさんのサーバ管理者向け無精のすすめ ~ちょっと便利なツールの紹介~というエントリで同期ツールの紹介がありましたが、ウノウでも同じような感じの独自ツールを作って同期をとっています。今回はこの同期ツールの紹介をしたいと思います。

僕が shell scripter ということもあってスクリプトは全て sh で。zsh の特殊な記法が使いたかったので zsh で書いています。

凡例

全てのコマンドは最後に対象とするホスト名を指定します。all というは特殊な指定で全てのサーバに対してリクエストが送られます。

共通関数

全てのスクリプトから読み込まれる共通関数です。

function absdirname()
{
        local dir=`dirname $1`
        if [ "x$dir" = "x." ]; then
                dir=$PWD
        elif ! echo $dir|grep '^/' > /dev/null; then
                dir=$PWD/$dir
        fi
        cd $dir
        echo $PWD
}

function absfilename()
{
        local dir=`absdirname $1`
        if [ "x$dir" = "x/" ]; then
                dir=""
        fi
        echo $dir/`basename $1`
}

function get_servers()
{
        local servers i=1 max=12
        while [ $i -le $max ]; do
                servers=(${servers[@]} "s$i");
                i=`expr $i + 1`
        done
        echo ${servers[@]};
}

dir_diff.sh directory host

ディレクトリ以下にあるファイルの diff を表示します。zsh の "<()" という記法を使ってます。これを使うと標準出力をファイルのように扱えて直接 diff に渡せるので便利です。

#!/bin/zsh
source `dirname $0`/sh_functions
PROGRAM_NAME=`basename $0`

function usage()
{
        echo Usage: $PROGRAM_NAME directory host
        exit $1
}

function _dir_diff()
{
        if [ -z "$1" -o -z "$2" ]; then
                echo "Lack of arguments" 1>&2
                return 1
        fi

        local source=`absfilename $1`
        diff -u <(ssh root@$2 find $source|sort) <(sudo find $source|sort)|grep '^\(+\|-\)'
}

# check arguments
if [ -z "$1" -o -z "$2" ]; then
        usage 1
fi

if [ "x$2" != "xall" ]; then
        _dir_diff $1 $2
        exit $?
fi
for host in `get_servers`; do
        echo ++++++++++++++++++++++++++++++++++++++
        echo + diff from $host
        echo ++++++++++++++++++++++++++++++++++++++
        _dir_diff $1 $host
        echo
done

rpm_diff.sh host

rpm パッケージの diff を表示します。サーバ間でパッケージの過不足がないかどうか確認するのに使います。

#!/bin/zsh
source `dirname $0`/sh_functions
PROGRAM_NAME=`basename $0`

function usage()
{
        echo Usage: $PROGRAM_NAME host
        exit $1
}

function _rpm_diff()
{
        if [ -z "$1" ]; then
                echo "You must specify a host name" 1>&2
                return 1
        fi

        diff -u <(ssh root@$1 rpm -qa|sort) <(rpm -qa|sort)|grep '^\(+\|-\)'
}

# check arguments
if [ -z "$1" ]; then
        usage 1
fi

if [ "x$1" != "xall" ]; then
        _rpm_diff $1
        exit $?
fi
for host in `get_servers`; do
        echo ++++++++++++++++++++++++++++++++++++++
        echo + diff from $host
        echo ++++++++++++++++++++++++++++++++++++++
        _rpm_diff $host
        echo
done

xdiff.sh source host

ファイルの diff をとります。設定ファイルがちゃんと同期されてるかどうか確認したりするのに便利です。

#!/bin/zsh
source `dirname $0`/sh_functions
PROGRAM_NAME=`basename $0`

function usage()
{
        echo Usage: $PROGRAM_NAME source host
        exit $1
}

# check arguments
if [ -z "$1" -o -z "$2" ]; then
        usage 1
fi

if [ "x$2" != "xall" ]; then
        _xdiff $1 $2
        exit $?
fi
for host in `get_servers`; do
        echo ++++++++++++++++++++++++++++++++++++++
        echo + diff from $host
        echo ++++++++++++++++++++++++++++++++++++++
        _xdiff $1 $host
        echo
done

xexec.sh command [options ...] host

host で指定されたサーバでコマンドを実行します。ライブラリが更新されたときに一気に全サーバで ldconfig 実行したりとか、他にもいろいろ使えます。

#!/bin/zsh
source `dirname $0`/sh_functions
PROGRAM_NAME=`basename $0`

function usage()
{
        echo "Usage: $PROGRAM_NAME command [options ...] host"
        exit $1
}

function _xexec()
{
        if [ -z "$1" -o -z "$2" ]; then
                echo "Lack of arguments" 1>&2
                return 1
        fi

        ssh root@$2 $1
}

# check arguments
if [ -z "$1" -o -z "$2" ]; then
        usage 1
fi

host=
command=
while [ -n "$1" ]; do
        command="$command $host"
        host=$1
        shift
done

if [ "x$host" != "xall" ]; then
        _xexec $command $host
        exit $?
fi
for host in `get_servers`; do
        echo ++++++++++++++++++++++++++++++++++++++
        echo + execute $command on $host
        echo ++++++++++++++++++++++++++++++++++++++
        _xexec $command $host
        if [ $? -ne 0 ]; then
                echo "Failed to execute on $host" 1>&2
                exit $?
        fi
        echo
done

xrsync.sh source host

その名の通り rsync します。ファイルを一斉にシンクロするのに便利です。

#!/bin/zsh
source `dirname $0`/sh_functions
PROGRAM_NAME=`basename $0`

function usage()
{
        echo Usage: $PROGRAM_NAME source host
        exit $1
}

function _xrsync()
{
        if [ -z "$1" -o -z "$2" ]; then
                echo "Lack of arguments" 1>&2
                return 1
        fi

        local opts source host
        while [ -n "$1" ]; do
                opts="$opts $source"
                source=$host
                host=$1
                shift
        done

        local source=`absfilename $source`
        local destination=`dirname $source`

        local opts="-auvz -e ssh $opts"
        eval sudo rsync $opts $source root@$host:$destination
}

# check arguments
if [ -z "$1" -o -z "$2" ]; then
        usage 1
fi

host=
arguments=
while [ -n "$1" ]; do
        arguments=(${arguments[@]} $host)
        host=$1
        shift
done

if [ "x$host" != "xall" ]; then
        _xrsync ${arguments[@]} $host
        exit $?
fi
for host in `get_servers`; do
        echo ++++++++++++++++++++++++++++++++++++++
        echo + rsync to $host
        echo ++++++++++++++++++++++++++++++++++++++
        _xrsync ${arguments[@]} $host
        if [ $? -ne 0 ]; then
                echo "Failed to execute on $host" 1>&2
                exit $?
        fi
        echo
done

xscp.sh source [source ...] host

こっちは scp 版。ちょっとしたファイルを同期したときに便利。

#!/bin/zsh
source `dirname $0`/sh_functions
PROGRAM_NAME=`basename $0`

function usage()
{
        echo "Usage: $PROGRAM_NAME source [source ...] host"
        exit $1
}

function _xscp()
{
        if [ -z "$1" -o -z "$2" ]; then
                echo "Lack of arguments" 1>&2
                return 1
        fi

        local sources host
        while [ -n "$1" ]; do
                sources=(${sources[@]} $host)
                host=$1
                shift
        done

        local opts="-pr"
        local source destination
        for source in ${sources[@]}; do
                source=`absfilename $source`
                destination=`dirname $source`
                eval sudo scp $opts $source root@$host:$destination
        done
}

# check arguments
if [ -z "$1" -o -z "$2" ]; then
        usage 1
fi

host=
sources=
while [ -n "$1" ]; do
        sources=(${sources[@]} $host)
        host=$1
        shift
done

if [ "x$host" != "xall" ]; then
        _xscp ${sources[@]} $host
        exit $?
fi
for host in `get_servers`; do
        echo ++++++++++++++++++++++++++++++++++++++
        echo + scp to $host
        echo ++++++++++++++++++++++++++++++++++++++
        _xscp ${sources[@]} $host
        if [ $? -ne 0 ]; then
                echo "Failed to execute on $host" 1>&2
                exit $?
        fi
        echo
done

まとめ

サーバ間の同期を取るためにウノウで導入しているツール群を紹介しました。こういう細かいツールを最初に作っておくと、作業が効率よく進み非常に楽になります。ぜひ、みなさんの環境でも導入してみてはいかがでしょうか。

何回か続けてきたベンチャー流サーバ構築のススメですが、おかげさまでみなさんから多くのトラックバック、コメント、ブックマークをいただきました。今回で大体サーバ構築に関するトピックは網羅したかと思います。何かありましたら、トラックバックなりコメントなりをいただければ幸いです。