ブログ

Amazon EC2のus-westにあるイメージをcronでS3に自動バックアップさせる方法

Amazon EC2にはインスタンスを実行する場所として、「eu-east」、「us-east」、「us-west」の3つがあります。
日本からアクセスする場合、一番近いアメリカ西海岸にあるus-westにインスタンスを作ったほうが通信速度が速いため、us-westにインスタンスを作りたくなります。

firefoxのS3用のアドオンであるs3foxがus-westに対応していないため、us-eastのS3に保存しようとしたのですが、
us-eastとまったく同じようにやると、いろいろつまずくことがありました。
ここではS3へのイメージファイルのバックアップをcronで自動実行させるときの問題とその解決方法を説明します。


普通、S3へのバックアップのステップは2つあり、
①ec2-bundle-vol コマンドでイメージファイルを作成します。
②ec2-upload-bundle コマンドでイメージファイルをS3へ転送。

この2つのコマンドを実行するだけです。

しかし、自分でコマンドを実行する分にはいいのですが、cronで実行しようとしたときに面倒くさいことが起こります。

何が面倒くさいかというと、us-westからus-eastのS3へイメージファイルを転送する際に、
ec2-upload-bundleを実行すると

[shell]
You are bundling in one region, but uploading to another. If the kernel
or ramdisk associated with this AMI are not in the target region, AMI
registration will fail.
You can use the ec2-migrate-manifest tool to update your manifest file
with a kernel and ramdisk that exist in the target region.
Are you sure you want to continue? [y/N]
[/shell]

というように対話形式になり、ここで「y」を入力しないと先に進まないのです。
自分でコマンドを実行する場合は問題ないですが、cronでこれを入力させて実行させるのに非常に悩みました。
結論から言うと「expect」というコマンドを使えば実現できるのですが、それがわかってからも結構苦労したので、ここで紹介。

まず、expect がインストールされていない場合はインストールしておきましょう。

[shell]
$ yum install expect
[/shell]

次にバックアップを実行するシェルスクリプトを作成。

ec2-bundle-volは問題ないので、その後から解説。
普通にec2-upload-bundleを実行すると、上でも述べたように対話形式になってしまいます。
そこで「expect」の出番です。

expectコマンドの詳細についてはこの辺を参考にしてください。
基本的な使い方は以下のようになります。
[shell]
expect -c "
spawn 実行したいコマンド
expect \"コンピュータが出力する文\"
send \"expectの内容に対して送信する内容\"
expect eof
"
[/shell]
(念のためexpectコマンド中のダブルクォーテーションを「\」でエスケープしてます)
というわけで、
[shell]
expect -c "
spawn ec2-upload-bundle –bucket バケット名 –manifest image.manifest.xml –access-key アクセスキー –secret-key シークレットキー
expect \"You are bundling in one region, but uploading to another. If the kernel
or ramdisk associated with this AMI are not in the target region, AMI
registration will fail.
You can use the ec2-migrate-manifest tool to update your manifest file
with a kernel and ramdisk that exist in the target region.
Are you sure you want to continue? \[y/N\]\"
send \"y\r\"
expect eof
"
[/shell]
とやってみました。

はい失敗。転送できてませんでした。

なんで。。。。?

いろいろ調べた結果、spawnで実行されるコマンドは別プロセスになるらしく、
ec2-upload-bundleは非常に時間がかかるので、このコマンドが終了する前にスクリプトが終了してしまい、
親プロセスが終了したことになるので、
その子プロセスであるec2-upload-bundleも完了する前に終了してしまってたらしい。

なので以下のように修正。
[shell]
expect -c "
spawn ec2-upload-bundle –bucket バケット名 –manifest image.manifest.xml –access-key アクセスキー –secret-key シークレットキー
expect \"You are bundling in one region, but uploading to another. If the kernel
or ramdisk associated with this AMI are not in the target region, AMI
registration will fail.
You can use the ec2-migrate-manifest tool to update your manifest file
with a kernel and ramdisk that exist in the target region.
Are you sure you want to continue? \[y/N\]\"
send \"y\r\"
wait $!
expect eof
"
[/shell]
sendの後にwaitを入れただけです。これで前のプロセスが終了するまでスクリプトは進みません。
結局終わってみればたいしたスクリプトじゃないんですが、実現させるのにかなり時間がかかってしまいました。。。

あと、ec2のコマンドがうまくいかない場合はオプションで「–debug」をつけてデバッグモードで実行させると
どこで失敗してるかが特定しやすくなりますよ。

参考までに、今回のスクリプトを改変して載せておきます。
[shell]
#!/bin/sh
#cronはほとんどパスが通ってないのでパスを通す。自分の環境にあわせて適宜修正
PATH=${PATH}:/home/ec2/bin
#以下も環境に合わせて適宜修正
export EC2_HOME=/home/ec2
export EC2_PRIVATE_KEY=プライベートキーのパス
export EC2_CERT=認証キーのパス

save_dir=イメージファイルの保存ディレクトリ
bucket_name=転送先のS3のバケット名
log=ログファイルのパス

#保存ディレクトリに移動
cd $save_dir
if [ $? != 0 ]; then
echo "ディレクトリ $save_dir は存在しません" >> $log
exit 1
fi

#イメージファイルの作成
echo `date` "start ec2-bundle" >> $log
ec2-bundle-vol -d $save_dir –privatekey プライベートキーのパス –cert 認証キーのパス –user 2189-4784-3414 –fstab /etc/fstab -r i386 2>>$log
if [ $? != 0 ]; then
echo "失敗" >> $log
exit 1
fi
echo `date` "imageファイルの作成に成功" >> $log
expect -c "
spawn ec2-upload-bundle –bucket $bucket_name –manifest image.manifest.xml –access-key アクセスキー –secret-key シークレットキー
expect \"You are bundling in one region, but uploading to another. If the kernel
or ramdisk associated with this AMI are not in the target region, AMI
registration will fail.
You can use the ec2-migrate-manifest tool to update your manifest file
with a kernel and ramdisk that exist in the target region.
Are you sure you want to continue? \[y/N\]\"
send \"y\r\"
wait $!
expect eof
"
echo `date` "バックアップ終了" >> $log
[/shell]

2010年04月05日  投稿者:yano  

▲ PAGE TOP