SoftLayer における Object Storage への Linux システム・バックアップ（バックアップ＆リストア編）

#0. はじめに
一般論として、Object Storage はアクセス頻度が低めで、大容量のコンテンツに向いているという話を良く聞きます。
この様な特徴はまさにバックアップ向けであり、Object Storage に対応したバックアップ・ソリューションが数多く発表されています。
一方、ツールが豊富な Linux 環境においては自力で (不具合修正や、実行結果の確認方法の確立、各種ガイドの作成まで、もれなく、各ユーザーの自力になりますが＾＾；) Object Storage へシステム・バックアップを取れるのではないかと考えたことが、この記事のきっかけです。

#1. 下準備
まず、SoftLayer 上で Linux 環境をオーダーするか、既に持っているものとします。
次に、その環境をカスタマー・ポータルからレスキュー・ブート(Device Details 画面から Action -> Rescue)で起動します。
すぐに再起動は完了しますが、その前に [ShinobiLayer: python-swiftclientを使ったSoftLayer Object Storageへのアクセス方法] 
(http://qiita.com/testnin2/items/c588da9c1658a2f176a8) で python-swiftclient の使い方を見ておくことをお勧めします。 

但し、以下の下準備の内容はレスキュー環境を再起動した時点で全て消去されます。
毎回やることなので、後で述べるバックアップ/リストア用のスクリプトとは別のスクリプトに組んで、他のサーバーや手元のPCから転送するとスマートに作業がすすめられます。

レスキュー･ブート完了後にログインすると、以下のようなメッセージが表示されます。

```
        NetworkLayer Linux Rescue Version 2.0

        Text Editors: ed, joe, nano, vim
        Command line web browsers: elinks, links, wget

        Raid Utilities:
         3ware: tw_cli (in path)
         Adaptec: arcconf (/usr/StorMan)
         LSI: MegaCli (/opt/MegaRAID)

        Recue Layer is CentOS based and yum functions.
        Additional packages can be added via yum as long
        as enough tmpfs space is available.
```

この環境は CentOS 5.8 ベースなので、ここに python-swiftclient をインストールします。

```
[linuxrescue -- **RESCUE**]# cat /etc/redhat-release
CentOS release 5.8 (Final)

[linuxrescue -- **RESCUE**]# wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/5/x86_64/epel-release-5-4.noarch.rpm
＜中略＞
2015-12-24 07:38:04 (3.79 MB/s) - `epel-release-5-4.noarch.rpm' saved [12232/12232]

[linuxrescue -- **RESCUE**]# rpm -ivh epel-release-5-4.noarch.rpm
warning: epel-release-5-4.noarch.rpm: Header V3 DSA signature: NOKEY, key ID 217521f6
Preparing...                ########################################### [100%]
   1:epel-release           ########################################### [100%]
[linuxrescue -- **RESCUE**]# ls
epel-release-5-4.noarch.rpm
[linuxrescue -- **RESCUE**]# rm epel-release-5-4.noarch.rpm
rm: remove regular file `epel-release-5-4.noarch.rpm'? y


[linuxrescue -- **RESCUE**]# yum -y install python26 python26-distribute --enablerepo=epel
＜中略＞
Installed:
  python26.x86_64 0:2.6.8-2.el5

Dependency Installed:
  libffi.x86_64 0:3.0.5-1.el5         python26-libs.x86_64 0:2.6.8-2.el5

Complete!
```

なんと、ここで、問題発生です。ちょっと前まで、インストールできていた、python26-distribute がダウンロードできなくなっています。

そこで、別途 pip インストール用のスクリプトをダウンロードします。

```
[linuxrescue -- **RESCUE**]# wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate
＜中略＞
2015-12-24 08:01:33 (40.9 MB/s) - `get-pip.py' saved
```

pip インストール用のスクリプトを実行します。

```
[linuxrescue -- **RESCUE**]# python26 get-pip.py
Collecting pip
＜中略＞
Successfully installed argparse-1.4.0 pip-7.1.2 setuptools-19.1.1 wheel-0.26.0
/tmp/tmpzQwXnk/pip.zip/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
```

セキュリティ関連の警告が盛大にでますので、本格利用する場合は、きちんと自力解決しておくことが必要ですが、この記事では、セキュリティ・リスクが存在していることを認識しつつ、先に進めます。

```
[linuxrescue -- **RESCUE**]# pip install python-swiftclient
Collecting python-swiftclient
＜中略＞
Successfully installed futures-3.0.3 python-swiftclient-2.7.0 requests-2.9.1 six-1.10.0

[linuxrescue -- **RESCUE**]# swift --version
python-swiftclient 2.7.0
```

とりえあず、レスキュー環境に python-swiftclient をセットアップすることができました。さらに curl を追加します。

```
[linuxrescue -- **RESCUE**]# yum install curl
＜中略＞
Installed:
  curl.i386 0:7.15.5-17.el5_9           curl.x86_64 0:7.15.5-17.el5_9

Dependency Installed:
  libidn.i386 0:0.6.5-1.1

Complete!
```

#2. バックアップ
環境に合わせて以下の bash スクリプト内を書き換えて実行します。
また、レスキュー環境のファイルシステムは全てメモリー上に作成されますので、最小構成の1GB RAM の仮想サーバーでもなんとか動作するように一時点での書き込む単位を 10MB に(ddコマンドの bs パラメーターで)制限しています。

```
#!/bin/bash
export ST_USER="Object Storage のユーザー名"
export ST_KEY="Object Storage のAPI Key (Password)"
export ST_AUTH="パブリックVLAN経由の場合: https://tok02.objectstorage.softlayer.net/auth/v1.0/ プライベートVLAN 経由の場合: https://tok02.objectstorage.service.networklayer.com/auth/v1.0/"
IMG_CON="システム・バックアップのイメージ・ファイルを格納するコンテナ名"
SourceDev="システムがインストールされているデバイス(仮想サーバーの例では、/dev/xvda)"
FileNamePrefix="ホスト名-`date -I`"
TmpFile=$(mktemp)

curl -I -H "X-Storage-User: ${ST_USER}" -H "X-Storage-Pass: ${ST_KEY}" -X GET ${ST_AUTH} > ${TmpFile}
sUrl=`gawk '$1 ~ /X-Storage-Url:/ {print $2}' ${TmpFile} | col`
sToken=`gawk '$1 ~ /X-Auth-Token:/ {print $2}' ${TmpFile} | col`
curl -kv -X PUT -H "X-Auth-Token: ${sToken}" -i "${sUrl}/${IMG_CON}"

for i in {0..99999}
do
  OutFile=$(printf ${FileNamePrefix}-%05d.img ${i})
  dd if=${SourceDev} of=${OutFile} bs=10M count=1 skip=${i}
  if [ -s ${OutFile} ]; then
    md5sum ${OutFile} > "${OutFile}.md5sum"
    gzip ${OutFile}
    swift upload "${IMG_CON}" "${OutFile}.gz" "${OutFile}.md5sum"
    rm -f "${OutFile}.gz" "${OutFile}.md5sum"
  else
    rm -f "${OutFile}"
    echo "end of ${SourceDev} detected."
    break
  fi
done
rm -f "$TmpFile"
echo "Backup to Container: ${IMG_CON} / Image file: ${FileNamePrefix} completed."
```

セキュリティ関連の警告が実行時にも出力されますが、以下の様なメッセージが表示されればバックアップの完了です。

```
Backup to Container: "システム・バックアップのイメージ・ファイルを格納するコンテナ名" / Image file: ”ホスト名-日付” completed.
```

#3. リストア用 bash スクリプト
> 2015/12/25 15:30 追記：
> 当初の CentOS6 環境を CentOS7 へ OS Reload後に、リストア後に fsck が正常に終了することで動作確認テストの一部としていたのですが、念の為、再起動までさせてみると、起動しない(原因は、[CentOS 6.x 仮想サーバーのカスタム化](http://qiita.com/MahoTakara/items/121ad43a778b2d04c4db) のコメントで言及しているカーネル･パラメーター console=hvc0 と思われます) ケースがあったので、カスタマーポータル上で登録されている OS と最終的にリストアして、動作させる OS は合わせておくことをお勧めします。
また、dd でのディスクの書きつぶしは、カスタマー・ポータルから仮想マシンの電源を入れられないケースまで出てきました。

> 上記の2つの例を合わせる考えると、バックアップ/リストア対象のデバイス上のファイルシステムに目印になるファイルを記録した上でバックアップを行い、同じバージョンへの OS Reload 後にリストアするのが一番面倒がすくないと判断しています。

リストアを行うには、バックアップと同じ手順で python-swiftclient ををセットアップします。リストアでは、curl は不要です。リストアの為に以下の bash スクリプト内を環境に合わせて書き換えて実行します。

```
#!/bin/bash
export ST_USER="Object Storage のユーザー名"
export ST_KEY="Object Storage の API Key (Password)"
export ST_AUTH="パブリックVLAN経由の場合: https://tok02.objectstorage.softlayer.net/auth/v1.0/ プライベートVLAN 経由の場合: https://tok02.objectstorage.service.networklayer.com/auth/v1.0/"
IMG_CON="システム・バックアップのイメージ・ファイルを格納するコンテナ名"
SourceDev="システムがインストールされているデバイス(仮想サーバーの例では、/dev/xvda)"
FileNamePrefix="ホスト名-バックアップの日付"

for i in {0..99999}
do
  InFile=$(printf ${FileNamePrefix}-%05d.img ${i})
  swift download "${IMG_CON}" "${InFile}.gz" "${InFile}.md5sum"
  if [ ! -e "${InFile}.gz" ]; then
    echo "There is no more image files for ${SourceDev}."
    break
  fi
  gunzip "${InFile}.gz"
  md5sum -c "${InFile}.md5sum"
  Md5sumRc=$?
  if [ ! "${Md5sumRc}" = 0 ]; then
    echo "A part of image file(${InFile}) and its md5sum(${InFile}.md5sum) are not valid."
    return
  fi
  dd if=${InFile} of=${SourceDev} bs=10M count=1 seek=${i}
  rm -f "${InFile}" "${InFile}.md5sum"
done
echo "Restore of ${SourceDev} completed."
```

バックアップ時と同じくセキュリティ関連の警告が大量に出力されます。以下の様なシステムがインストールされているデバイスに対するメッセージが表示されればリストアの完了です。

```
"Restore of /dev/xvda completed."
```

#4. 参考リンク
[Grizzlyの新機能(2) SLO ～ Swiftによるオブジェクトストレージシステムの構築(10) ](http://blog.bit-isle.jp/bird/2013/06/35)
[bashで指定回数ループして綺麗なファイル名を作る](http://takuya-1st.hatenablog.jp/entry/20090217/1234871797)
[Cent OS 5.9 に yum を使って Python 2.6 をインストール](https://takatamajp.wordpress.com/2013/06/17/install_python26_to_centos59_by_yum/)


#5. 蛇足と言い訳
本来であれば、最初からリストア方法および結果の確認まで含めた記事にする予定だったのですが、慣れていたつもりの bash も久しぶりで時間がかかるわ、EPEL にあるはず(あったはず？)のパッケージが無いわ、で、12/25 00:00 時点ではサンタさんが悪い子に渡す、「石炭の塊」の様な記事になってしまいました（もちろん、皆さんでは無くて私が悪い子だったんです＾＾；）。

それでも、何とかめげずに、「爪に火を点す様なバックアップ＆リストア」のリストアの項目を2015年のうちに追記することができました。

> 2015/12/25 追記：
> 改めてログを確認するとレスキュー環境のファイルシステムの小ささを甘く見た為に、急遽 1 セグメントの大きさを 10MB へ変更した結果、SLO のセグメント数上限に引っかかっていました。("Number of segments must be <= 1000")。
>> 2016/01/04 追記：
>> 上記を含めた Object Storage の各上限設定値は、"swift capabilities" コマンドで確認可能です。

```
# swift capabilities
Core: swift
 Options:
＜中略＞
  max_file_size: 5368709122
＜中略＞
  max_meta_value_length: 256
  max_object_name_length: 1024
＜中略＞
  strict_cors_mode: True
  version: 2.2.2
＜中略＞
Additional middleware: slo
 Options:
  max_manifest_segments: 1000
  max_manifest_size: 2097152
  min_segment_size: 1048576
＜以下略＞
```

> 当初、SLO (1セグメント辺り 100MB) の前提でスクリプトを作成しましたが、[マニュアル](http://docs.openstack.org/user-guide/cli_swift_large_object_creation.html) の "Comparison of static and dynamic large objects" の表中の "Segment object size and number" の様に SLO ではセグメント数の省略時値は 1000 です。
結局、SLO も DLO も利用せず(もう誰にも頼らない＾－＾)、ついでに圧縮してから Object Storage へ格納する方式へ仕様変更しました。
当初もくろんでいた、「swift コマンドの出力を直接 dd コマンドに渡すだけ！」の様なリストア時の楽はできず、リストア時にもスクリプトが必要になりました。
> しかし、Object Storage の消費容量も大幅に減り(25GB -> 800MB強)、転送量が減少することで数時間かかっていたバックアップ時間も1時間程になりました。

> 但し、これはあくまでデプロイ直後で大幅に圧縮が効く条件下での値です。実際には、最低でも実ディスク容量の半分程度の容量と数時間単位でのバックアップ・ウィンドウを用意すべきと考えています。
>>2015/12/25 17:25 追記
>> 今回テストに使用したシステムから Standard Image を作成すると(Thin Provisioning の仕組み的には当然かもしれませんが) 非常に大きくなっていたので、以前書いた[SoftLayer Standard Image のサイズ](http://qiita.com/conservativerad/items/329b9f359894f202ce31) に記載を追加しました。

以上、「イブの夜とクリスマスを SoftLayer と過ごす」の巻でした。