Ubuntu

Ubuntu18.04 データーを無理なくゆっくりバックアップする

以前、バックアップがちゃんと取れていないことで痛い目に遭ってからバックアップを取るようにはしていたのだけれど、問題がある。ESXiに複数のゲストサーバーを動かしているが、サーバーの1つが【真剣に】バックアップをはじめると、サーバー全体のレスポンスが悪くなるのだ。



広告


今回、問題を整理してちゃんと動くスクリプトを作ることにした(恥ずかしいスクリプトだが自分用メモとして最後に載せてある)。

ボトルネックはHDDのI/Oとネットワーク帯域。無理なくゆっくりバックアップ!なんて考えたから、世の中にあるツールをそのまま使う訳にはいかなくて、手作業で色々解決することになりそうな気配。

やること。

なお、今回の記事で行う操作には全て root ユーザーを使った(コマンドラインの先頭が#)。

 

バックアップの課題

さて、現在のバックアップはというと…cronからスクリプトを呼び出し、処理結果は「スクリプトが標準出力に出した文字」がメールで送られてきている。

…
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

m h dom mon dow user command
…
# backup
30 3    * * *   root    /root/backup_script

これが幾つかの課題を抱えていた。

処理時間が分からない

いつ開始して、いつ終わったのか、所要時間が分からない。夜間バッチの計画は超適当な勘。

バックアップファイル作成中にHDDのI/O帯域を使い切る

バックアップファイルを作っている間、処理が激しすぎる。

  • データーベースのダンプを取っているとき。
  • デーテーベースのダンプをzipで固めているとき。
  • 大量のファイルをzipで固めているとき。

ファイル転送でネットワーク帯域を使い切る

バックアップを作った後でNASにファイルを送るのだが、これが仮想サーバーのネットワークの帯域を使い切ってしまう。

世代管理ができていない

世代管理の処理を組むのが面倒…ただそれだけの理由でいつもバックアップは1世代だった。

バックアップスクリプトの改善

それぞれの課題を解決する。

処理時間を明確にする

処理開始時間を標準出力に出力すれば、クーロンがその内容をメールで送ってくれる。
Qiita / bashのSECONDS変数で簡単に処理時間を測定する
Qiita / bashの計算式で、秒数をHH:MM:SSフォーマットに変換する

#!/bin/bash

# 処理開始時刻の表示と記録
echo "処理開始" `date`

~バックアップ処理~

# 処理終了時刻の表示
echo "処理終了" `date`

# 全体の処理時間を表示
i=$SECONDS
((sec=i%60, min=(i%3600)/60, hrs=i/3600))
echo $(printf "処理時間 %d:%02d:%02d" $hrs $min $sec)

最初、時間を覚えておいて計算させようかと思ったんだけど、SECONDS変数を教えてくれたので、それを利用。

バックアップファイルをゆっくり作る

バックアップファイルを全力で作るとHDDのI/Oが凄い。凄すぎて、他の処理がまともに動かない(と思っていた)。

プロセスのCPU占有率を調整できるコマンドcpulimitを使ってみよう。
まくまくLinux/Shellノート / Deep Learning や仮想通貨のマイニング時に CPU 使用率が 100% になってしまうのを防ぐ (cpulimit)

I/Oの優先度を調整できるコマンドioniceも考えたけれど、ファイルをゆっくり作れば、当然I/Oの占有率も下がるだろう。
How old are you? / 大量・巨大なファイル操作を低負荷で行う方法(リンク切れのためアーカイブへ)

cpulimitコマンドをインストールする。

# apt install cpulimit

早速、各種処理で試してみる。

mysqldump

Kopano(Exchangeっぽいサーバー)をもう8年?くらい運営している。使用頻度は低いのだが、700M程のデーターがある。

cpulimit -l 10 -f -- \
 mysqldump --single-transaction --routines kopanoserver > /root/backup/kopanoserver.dmp

※赤文字部分でCPU使用率を制限。-l 10 で 10%、-fで処理終了を待つ。--はcpulimitのパラメータがココまでであることを示し、その後に書かれているコマンドが実行される。

これを実行しvmstatで様子を見たところ、平均値はこんな感じに。

指定処理時間CPU使用率(平均)ディスク入力(平均)ディスク出力(平均)
なし0:2128%19,450 block/s16,210 block/s
10%1:2013%5,835 block/s10,026 block/s

zip

mysqldumpで出力したデーターベースを圧縮している。700MBのデーターが80MB程に圧縮される。

指定処理時間CPU使用率(平均)ディスク入力(平均)ディスク出力(平均)
なし0:2340%31,435 block/s2,501 block/s
10%2:0311%5,963 block/s1,903 block/s

ファイルをゆっくり転送する

その後のローテーション処理とかを考えると、バックアップ先をmountして扱うのが楽かなと思った。

バックアップ先のマウント

バックアップ用にNAS(Linkstation)を使い、SMB2.0接続する。
まだ、cifs-utilsをインストールしていないなら、ココでインストール。

# apt install cifs-utils

バックアップスクリプトの中で、ファイル転送時にマウントする。

#!/bin/bash
…
# ユーザー・パスワード取り込み
source /root/backup_secret

mount -o username=$BK_USER,password=$BK_PASS,vers=2.0 //linkstation/backup /root/backup/linkstation

# ファイルを保管

umount /root/backup/linkstation

※ホスト名はlinkstationとしていて、名前解決ができるようになっている。

ユーザーとパスワードは別ファイルに書いて取り込むようにした。パーミッションは600。/root配下にある時点で、他のユーザーからは見えないかもしれないけど。

/root/backup_secret

BK_USER=<SMB接続用ユーザー>
BK_PASS=<SMB接続用ユーザーのパスワード>

 

コピー

どうにかして「ゆっくり」ファイルをコピーする方法はないだろうか…遅いのを早くするということに関する記述は結構あるけど、早いのを遅くするってのはかなり貴重な情報だった。
StackExchange / Make disk/disk copy slower

何らかの方法で pv というコマンドに中身を通し、pvコマンドが持つ帯域制限機能を利用して出力スピードをコントロールする模様。

時間は2倍になるけど、使用帯域を50Mbps(=6,400KB/s)に制限してみる。
まずは、pvコマンドをインストール。

# apt install pv

バックアップスクリプトの中に以下の関数を作ってみた。

function SlowCopy() {
    cat $1 | pv -q -L 6400k > $2
}

※エラー処理なし、速度を調整しながらsourceからdestinationにデータを送り込むのみ。
※1つめの引数($1)はコピー元ファイル、2つめの引数($2)はコピー先ファイル。

ここで、コピー速度を比較してみたところ、狙い通りとなった。ただし、通信速度は一定ではなく、時々早くなり、早すぎると止められる…というのを繰り返しているようだった。

コピー方法コピー時間帯域利用(平均)
cp0:5992Mbps
cat | pv > target1:5747Mbps

世代管理

バックアップのファイル名は「ホスト名+ベース名+日付」として作っていき、指定した期間(日数)を過ぎた古いファイルを消す。

ファイル名

NASに保管するバックアップファイルの名前は、元ファイル名から加工するようにした。

BK_DESTDIR=/root/backup/linkstation ←バックアップ先
BK_PREFIX=hogehoge                  ←ホスト名にしてみた
BK_SUFFIX="date +%Y-%m-%d-%H-%M-%S" ←日時をくっつけることにした

BK_BASE=`basename $FULLPATHNAME`
BK_NAME=${BK_BASE%.*}
BK_EXT=${BK_BASE##*.}

BK_DESTNAME=$BK_DESTDIR/$BK_PREFIX.$BK_NAME.`$BK_SUFFIX`.$BK_EXT

FULLPATHNAMEに /root/backup/backupdata.zip を入れてこれを通すと、
/root/backup/linkstation/hogehoge.backupdata.2020-05-05-13-50-21.zip といったファイル名が生成できる。

古いファイルの削除

古いファイルの削除は、タイムスタンプを利用して実行。
Qiita / 指定日時より古いファイルの削除コマンド

BK_DESTDIR=/root/backup/linkstation
BK_PREFIX=hogehoge
BK_RETENTIONDAYS=7

find $BK_DESTDIR/ -mtime +$((BK_RETENTIONDAYS-1)) -name $BK_PREFIX.backupdata.*.zip   | xargs rm -f

これで簡単に古いファイルが削除でき、世代管理っぽい処理ができるようになった。

動作確認

リソースの利用状況は過去に設置したZabbixで確認
ウチはネットワークが100Mbps、外部記憶装置はHDDという貧弱な構成。もっと良い装置ならばボトルネックも変わり、違った結果になると思う。

CPU使用率10%、ファイル転送速度6.4MB/sで、Wordpressの6.7GBをバックアップした結果がこちら。写真やビデオなんかをふんだんに載せているのでそれなりの容量。

それにしても…だ。50分は長いし、CPU、ディスクI/O、ネットワークのリソースをもうちょっと使っても良さそう。

そこで、CPU使用率40%、ファイル転送速度を9.6MB/sに変えた結果がこちら。

18分、ファイル転送をもうちょっとだけ落としても良さそうだけど、他の処理もギリギリやれそうな予感。


ネットワーク越しにバックアップを直接取るのであれば、trickleという手がある模様。
CodeFlow / mysqldumpが使用する帯域幅を制限する方法

やったこと

CPUのコア数を取得

cpulimitを使用するにあたり、コア数を意識した方が良いかと思ったが…そもそも、ディスクI/Oを減らそうとするなら、使用するコアは1つだけの方が良いのだろうと思われた。

その結論に至る過程で、コア数を取得する方法を見つけた。プロセッサーを表示させ、行数を数えている。
Red Hat Customer Portal / 物理 CPU、CPU コア、および論理 CPU の数を確認する

# grep processor /proc/cpuinfo | wc -l
2

 

帯域制御

ファイルをゆっくりコピーする方法が見つからず、ネットワーク帯域を制御することを考えた。

コピー速度を下げたいけれども、それを実現する方法は…これか!
VITUX / How to Limit Network Bandwidth in Ubuntu
IT Blog / Installing and using the Wondershaper
GitHub / magnific0 / wondershaper

ここまで来たところで、WondershaperはNICごとに通信速度を下げてしまうので、Wondershaperを実現しているtcコマンドで特定の通信だけをコントロールするべきだと気付く。
Gree Engineering / Linux TC (帯域制御、帯域保証) 設定ガイドライン
hana_shinのLinux技術ブログ / tcコマンドの使い方(移転のようなのでリンク張り直し)

tcコマンドは帯域を上手くコントロールできそうだということは分かったのだが、tbf(Token Bucket Filter)を見たら、バースト用のバッファー量とか、バケツからあふれたら破棄されるとかいう説明を見て、かなり計画的にやらないと今回の目的は達成できないかもしれないと考え、他の方法を探すことにした。

とりあえず運用することにしたスクリプト

エラー処理も何もない、ただただまっすぐ走るだけのスクリプト。恥ずかしいけれども、自分用メモとして。

#!/bin/bash

#
# 各種設定
#

# ユーザー・パスワード取り込み
# BK_USERとBK_PASSはbackup_secretに書いてある
source /root/backup_secret

# CPU使用率
BK_CPUPOWER=15
BK_COPYBWDT=6400k
#BK_CPUPOWER=40    ← WordPressの6.7GBをバックアップするときはこれ
#BK_COPYBWDT=9600k ← 家族用のブログで更新頻度が低いので週次で実施

BK_BASEDIR=/root/work/backup
BK_DESTDIR=/root/work/backup/linkstation
BK_PREFIX=hogehoge
BK_SUFFIX="date +%Y-%m-%d-%H-%M-%S"

BK_RETENTIONDAYS=7
#BK_RETENTIONWEEKS=5 ← WordPressは5週間保管

#
# 関数
#

function OutputSeconds () {
    i=$SECONDS
    ((sec=i%60, min=(i%3600)/60, hrs=i/3600))
    echo $(printf "経過時間 %d:%02d:%02d" $hrs $min $sec)
}

function SlowCopy() {
    SC_BASE=`basename $1`
    SC_NAME=${SC_BASE%.*}
    SC_EXT=${SC_BASE##*.}

    SC_DESTNAME=$BK_DESTDIR/$BK_PREFIX.$SC_NAME.`$BK_SUFFIX`.$SC_EXT

    cat $1 | pv -q -L $BK_COPYBWDT > $SC_DESTNAME
}

function RetentionBaseName() {
    SC_BASE=`basename $1`
    SC_NAME=${SC_BASE%.*}
    SC_EXT=${SC_BASE##*.}

    SC_DESTNAME=$BK_DESTDIR/$BK_PREFIX.$SC_NAME.*.$SC_EXT

    echo $SC_DESTNAME
}


# 処理開始時刻の表示と記録
echo "処理開始" `date`


#
# バックアップデーター作成
#

# Kopano
echo "Kopano  " `OutputSeconds`

rm $BK_BASEDIR/kopanoserver.dmp
rm $BK_BASEDIR/kopano.zip

cpulimit -l $BK_CPUPOWER -f -- \
 mysqldump --single-transaction --routines kopanoserver > $BK_BASEDIR/kopanoserver.dmp

cpulimit -l $BK_CPUPOWER -f -- \
 zip -rq $BK_BASEDIR/kopano.zip \
  $BK_BASEDIR/kopanoserver.dmp \
  /var/lib/kopano/attachments


# Alfresco
echo "Alfresco" `OutputSeconds`

rm $BK_BASEDIR/alfresco.dmp
rm $BK_BASEDIR/alfresco.zip

cpulimit -l $BK_CPUPOWER -f -- \
 mysqldump --single-transaction alfresco > $BK_BASEDIR/alfresco.dmp

cpulimit -l $BK_CPUPOWER -f -- \
 zip -rq $BK_BASEDIR/alfresco.zip \
  $BK_BASEDIR/alfresco.dmp \
  /usr/share/alfresco/alf_data/contentstore \
  /usr/share/alfresco/alf_data/contentstore.deleted


#
# データ転送と世代管理
#

echo "Transfer" `OutputSeconds`

mount -o username=$BK_USER,password=$BK_PASS,vers=2.0 //linkstation/backup $BK_DESTDIR

SlowCopy $BK_BASEDIR/kopano.zip   $BK_DESTDIR
SlowCopy $BK_BASEDIR/alfresco.zip $BK_DESTDIR

find $BK_DESTDIR/ -mtime +${BK_RETENTIONDAYS-1} -name $BK_PREFIX.kopano.*.zip   | xargs rm -f
find $BK_DESTDIR/ -mtime +${BK_RETENTIONDAYS-1} -name $BK_PREFIX.alfresco.*.zip | xargs rm -f
# WordPressを週次バックアップにしようとして計算式の書き方を変更
#find $BK_DESTDIR/ -mtime +$(($BK_RETENTIONWEEKS*7-1)) -name $BK_PREFIX.wordpress.*.zip | xargs rm -f

umount $BK_DESTDIR


#
# 終了処理
#

echo "処理終了" `date`
OutputSeconds

世の中にはもっとかっこいいスクリプトがあるはず。今回は「遅くする」ってのを主眼にしているので、奇妙さは醸し出しているだろう。

実行結果として、以下のメールが送られてきていた。もうちょっとかっこいい表示の仕方ってものもあるだろうけれど、動き自体は狙い通りなので良しとする。

Subject: Cron <root/hogeserver> /root/backup_daily
From: Administrator <root@hogeserver.hogeddns.jp>

処理開始 Wed May 6 03:30:01 JST 2020
Kopano 経過時間 0:00:00
Process 15355 detected
Child process is finished, exiting…
Alfresco 経過時間 0:03:41
Process 15397 detected
Child process is finished, exiting…
Transfer 経過時間 0:06:03
処理終了 Wed May 6 03:38:16 JST 2020
経過時間 0:08:15

 

さいごに

正直なところ、バックアップスクリプトの作成くらいでこんなに時間が掛かるとは思っていなかった。

世の中的に変わったことをやろうとしているということは、それが必要ないことなのであって、きっともっと良い方法があるのだろう。今回は見つけられなかったけど。

サーバーがSSDでGBイーサーならば悩まないのかもしれないし、クラウドサーバーなら気付かないかも。

ウチの設備は古い。でも、利用者が家族や親戚だけなんだから、これくらいの調整ができれば十分に快適だ。

広告

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他