[AWS]S3へのバックアップで使うAWS CLI S3 syncはrsyncと挙動が異なるので注意

AWS
[AWS]S3へのバックアップで使うAWS CLI S3 syncはrsyncと挙動が異なるので注意

本当に便利ですよね、S3。

VPCエンドポイントを経由すると同じリージョンの EC2 – S3 間の通信料が無料となるので、滅多に取り出さないバックアップ用途には最適です。

S3へのバックアップで欠かせないのが AWS CLI S3 sync コマンドですが、おなじみ rsync コマンドとは挙動が少し異なるので注意が必要です。

syncの標準出力の改行コードがCR

sync コマンドの標準出力をログファイルにリダイレクトすると、

aws s3 sync --delete --exact-timestamps {EC2のバックアップ元パス} s3://{S3のパス} > ${LOGFILE} 2>&1

改行コードがなぜかCRなのです。
改行コードCR

^MがCRです。

余計な行をgrepで除外できないので困る

syncコマンドは、ファイル同期の途中経過も標準出力します。

Completed 645.3 KiB/652.1 KiB with 8 file(s) remaining
Completed 645.4 KiB/652.1 KiB with 8 file(s) remaining
upload: {EC2パス} to {S3パス}

ファイルが正常に同期できたかどうかだけ知りたいので、1行目と2行目は不要です。それにAmazon SESでログをメール送信するので、文面を短くしたいところ。

不要行を除外するためにこんな感じでgrepしたいのですが、改行コードがCRなので、ログファイル全体が1行として認識されてしまいます。

$(cat ${LOGFILE} | grep -vi '^Complete.*'

改行コードの変換が必要

ちゃんと除外するには改行コードの変換(CR→LF)を挟まなければなりません。

$(cat ${LOGFILE} | tr \\r \\n | grep -vi '^Complete.*')

【余談】Amazon SESでメールするなら改行コードに注意

ログを Amazon SES でメール送信する場合、改行コードを LF から CRLF に変換する必要があります。

aws ses send-email --from {from} --to {to} --subject "[azs backup]"`date "+%Y%m%d"` --text "$(cat ${LOGFILE} | tr \\n \\r\\n)"

LF のままでは受信メール表示時に改行されません。

–exact-timestampsオプションはあった方がいい

sync コマンドの –exact-timestamps オプションはあった方がいいです。

aws s3 sync --delete --exact-timestamps {EC2のバックアップ元パス} s3://{S3のパス} > ${LOGFILE} 2>&1

これが無いと、ファイルサイズだけで変わったかどうか判断されてしまいます。つまり、内容が変わっていてもファイルサイズが変わっていなければ同期されません。

rsync コマンドはファイルサイズとタイムスタンプで判断するので、挙動が異なりますね。

このオプションを付けるとタイムスタンプも判断材料となるので、ファイルサイズが同じでも同期されます。

バックアップ対象のファイル名が都度変わるのであれば –exact-timestamps は不要です。

ファイルサイズがゼロのファイルは同期されない

ファイルサイズがゼロ(つまり空っぽ)のファイルは同期されません。

空のファイルを制御に使っている場合は注意

例えばシステム連携にS3を使っている場合です。かなりベタですがよく見る処理です。

S3に特定の制御ファイルがあったら連携データがアップロードされたと判断して、連携データをダウンロードする。

連携

ここで制御ファイルとしてゼロサイズのファイルを生成する場合、①で同期されません。当然②で検知できないので③の処理は行われません。

エラーとなる

同期されないだけならまだ良いのですが、以下のようなエラーが出力されます。

-e upload failed: {パス}/空のファイル.txt to s3://{パス}/空のファイル.txt seek() takes 2 positional arguments but 3 were given

エラーログを検知してメール通知するような場合、邪魔になります。

AutoMySQLBackupとの相性は悪い

弊社の一部では MySQL のバックアップに AutoMySQLBackup スクリプトを使っています。

困ったことにこのスクリプトは正常終了でも空のエラーログファイルを出力します。

仕方がないので、処理の最後で空ファイルを削除するようにしました。

###################################################################################
# Clean up and finish

#    [[ -e "$log_file" ]] && rm -f "$log_file"
#    [[ -e "$log_errfile" ]] && rm -f "$log_errfile"

# remove zero size files
find "$CONFIG_backup_dir" -type f -empty -delete

中身が空のディレクトリは同期されない

空のディレクトリは同期されません。ファイルと違いこちらはエラーとはなりません。

rsync --prune-empty-dir

と同じ挙動ですね。

同期元のパスの最後の”/”はあっても無くても同じ

よく使う rsync コマンドでは、同期元のパスの最後に”/”があるか無いかで挙動が変わります。これを間違えて同期先のファイルが消えてしまった人もいるのではないでしょうか。

しかし sync コマンドでは”/”があっても無くても rsync コマンドの”/”ありと同じ挙動となります。

# どっちも同じ
aws s3 sync /home/azs/backups/  s3://azs/backups/
aws s3 sync /home/azs/backups  s3://azs/backups/

同期対象は /home/azs/backups/ 配下のディレクトリ・ファイルです。

つまり s3://azs/backups/backups/ にはなりません。

–deleteオプションを付ける前に–dryrun

これは rsync の場合と同じですね。

ミスって同期先から大事なファイルが消えてしまわないように –dryrun で必ずチェックしておきましょう。

最後に

rsync に似せているのですから、なるべく同じ挙動にしてほしかったのが正直なところ。

いずれにしろ S3 へのバックアップでは非常に有用なコマンドなのは確かです。注意して使いましょう。