2016年3月30日水曜日

システムバックアップその9

ようやく、バックアップ処理のメインである「ファイル・ディレクトリ一式のバックアップ」処理だ。
ここまで永かった…。
 
お約束通り、まずはスクリプトだ。
sudo su - して root 権限を取得しておくのを忘れないようにね。
# cd
# cd backup/bin
# vi 0050_create_archive

中身はこんな風に
--中身ココから--
#!/bin/sh

BINDIR=`/usr/bin/dirname $0`
BINDIR=`/usr/bin/realpath ${BINDIR}`
ETCDIR=`/usr/bin/realpath ${BINDIR}/../etc`

. ${ETCDIR}/config

# Backup Start
for FSCONFIG in ${ETCDIR}/fsconfig/*
do
  VOLNAME=""
  USELVM=""
  MOUNTPOINT=""

  . ${FSCONFIG}

  . ${BACKUPPOINT}/current/config/blkid/${VOLNAME}

  if [ x${USELVM} = "xNo" ]
  then
    /bin/mount --bind ${MOUNTPOINT} \
                      ${BACKUPPOINT}/current/snapshot
    /bin/tar -cj \
             -f ${BACKUPPOINT}/current/archive/${VOLNAME}.tbz \
             -C ${BACKUPPOINT}/current/snapshot \
                .
    /bin/umount ${BACKUPPOINT}/current/snapshot
  fi

  if [ x${USELVM} = "xYes" -a x${TYPE} != "xswap" ]
  then
    /bin/mount -o ro /dev/${ROOTVG}/${VOLNAME}.snap \
                     ${BACKUPPOINT}/current/snapshot
    /bin/tar -cj \
             -f ${BACKUPPOINT}/current/archive/${VOLNAME}.tbz \
             -C ${BACKUPPOINT}/current/snapshot \
                .
    /bin/umount ${BACKUPPOINT}/current/snapshot
  fi

done

--中身ココまで--
これまでのスクリプトを読んでいれば、こちらはそんなに複雑ではないはずだ。
最初に基本変数を読み込んで、fsconfigの下にある設定ファイル分だけループを回す。
そのループの中で、
  • LVMを使用していない場合
    対象の領域を、current/snapshot へ bindオプション付きでマウント。
    tar で、当該領域を吸い上げ、current/archive ディレクトリへ書き込み。
    終わったらアンマウント。
  • LVMを使用している&当該領域がswapではない場合
    対象領域の snapshot 領域を、current/snapshot へリードオンリーでマウント。
    tar で、当該領域を吸い上げ、current/archive ディレクトリへ書き込み。
    ​終わったらアンマウント。
という処理を行っているだけだ。
ここで初出の変数は(多分)一つだけ。
${MOUNTPOINT}
だ。
 
これは、mount --bind と合わせて使用している。
既にマウント済みの領域は、他のマウントポイントにはマウント出来ない。
LVM領域の場合は、snapshot を作成した上で、その snapshot からファイルを吸い上げるので、特に関係は無い。
問題なのはLVM以外の領域(具体的には、sda1 / sda2 の2領域)だ。
こちらは、既にマウントされている状態のため、current/snapshot へマウントすることが出来ない。
そこで出てくるのが --bind オプションだ。
このオプションは、ある特定のディレクトリ、ファイルを別の場所に再マウントしたように見せかけるオプションだ。
(ファイルの場合、ハードリンクでもいいと思う人もいるかもしれないけど、ハードリンクは同一のファイルシステム内でしか利用出来ない。)
これによって、/boot や /boot/efi 等のディレクトリ以下を、current/sunapshot へ再マウントしているかのように見せかけている。
しかも、デフォルトでは下位マウントポイントまではマウントされない。
つまり、 mount -o bind /boot ..../current/snapshot とマウントしても、/boot/efi の下のファイルは .../current/snapshot/efi の下には現れないんだ。
これによって、/boot ファイルシステムのみを、.../current/snapshot に見せることが出来る。
バックアップの形式には tar を用いている。
Unix系OS界隈では、「バックアップに tar を用いてはいけない」という都市伝説がある。
実際、「tar ではなく cpio を用いるように」とか、dumpコマンドでバックアップを取るやり方を紹介しているところが多い。
確かに tar は、アーカイブファイル(バックアップファイル)の途中が破損した場合、その破損部以降は全て読み取ることが出来ない。
それに対して cpio は、その破損部より後ろでも、正常な部分は読み取ることが可能だ。
そういう理由で、tar ではなく cpio を薦めている人が多い。
 
だけどちょっと考えてみて欲しい。
今回取得しようとしているのは、OSのバックアップだ。
OS領域をリストアした結果、アーカイブファイル(バックアップファイル)の一部が破損していたので、破損部以外をリストアしました、というのは、そのシステムを安心して使えるだろうか?
一部が壊れているのであれば、それはアーカイブファイル全体が壊れていると考えた方が安全ではないだろうか?
ということで、cpio の優位性の一つはあまり優位ではないと考えている。
ついでに tar を使用している理由としては、以前 Linus Tovalds が「バックアップは tar を使え」と言っていた記事を見たからだ。(その記事がドコにあるか、もはや覚えていないが…)
 
ちなみに、ファイルシステムに xfs を使用している場合は、別の手順になると思われる。
一応、想定で書くと、以下のブロックを追記しておく必要があるのではなかろうか?
--ココから--
  if [ x${USELVM} = "xYes" -a x${TYPE} = "xfs" ]
  then
    /bin/mount -o ro,no-uuid /dev/${ROOTVG}/${VOLNAME}.snap \
                     ${BACKUPPOINT}/current/snapshot
    xfsdump -l 0 \
             -f ${BACKUPPOINT}/current/archive/${VOLNAME}.tbz \
             ${BACKUPPOINT}/current/snapshot \
    /bin/umount ${BACKUPPOINT}/current/snapshot
  fi

--ココまで--
合わせて、ext4をバックアップしているところも、以下のように書き換えておく必要が有ると思われる。
--ココから--
  if [ x${USELVM} = "xYes" -a x${TYPE} != "xswap" ]
  ~
  fi


  if [ x${USELVM} = "xYes" ]
    if [    x${TYPE} = "xext2"
         -o x${TYPE} = "xext3" 
         -o x${TYPE} = "xext4" ]
    ~
    fi
  fi

--ココまで--
動作検証一切していないので、動く自信は一切ない。注意。
 
さて、変数の話に戻って、${MOUNTPOINT}だ。
これは、ファイルシステム毎に定義するべき内容なので、定義先は etc/fsconfig/* になる。
今は3つ定義ファイルがあると思うので、それぞれ以下の内容を追記してしまおう。
# vi ../etc/fsconfig/0010_sda1
--追記ココから--
#
# MOUNTPOINT:
# Specify the mount point of the target area.
#
MOUNTPOINT=/boot/efi

--追記ココまで--
 
# vi ../etc/fsconfig/0020_sda2
--追記ココから--
#
# MOUNTPOINT:
# Specify the mount point of the target area.
#
MOUNTPOINT=/boot

--追記ココまで--
 
# vi ../etc/fsconfig/0030_lv-root
--追記ココから--
#
# MOUNTPOINT:
# Specify the mount point of the target area.
#
MOUNTPOINT=/

--追記ココまで--
 
変数定義も出来たので、動作確認してみよう。
例によって、実行パーミッションを付与しておくところから。
# chmod +x 0050_create_archive
# ./0010_mount
# ./0020_mkdir
# ./0030_create_config
# ./0040_create_snapshot
# ./0050_create_archive

(0050_create_archive は少し時間がかかる。何も画面変化が無いけど、慌てないように。数分~20分も待てば、プロンプトが返ってくるはずだ。)
(人によっては、「/bin/tar: ./ssh-XXXX/agent.NNNN: ソケットは無視します」のようなメッセージが出ているかもしれない。これはソケットファイルというもので、sshでログインしていて、agent転送を許可している場合に自動的に作られるソケットファイルだが、バックアップする必要が無いものなので、気にしなくてイイ。)
無事に処理が完了したら、アーカイブファイルが出来上がっているはずだ。
ファイルは、current/archive の下にあるはず。見てみよう。
# ls -l /media/backup/current/archive
多分、sda1.tbz、sda2.tbz、lv-root.tbz という3つのファイルが出来ているのではないだろうか?
それぞれ、ちゃんとファイルが格納されているか、ファイル一覧も見てみよう。
# tar tvjf /media/backup/current/archive/sda1.tbz
# tar tvjf /media/backup/current/archive/sda2.tbz
# tar tvjf /media/backup/current/archive/lv-root.tbz

ファイルのバックアップは無事に完了だ。
必要な領域分、定義ファイルを作成しないといけないが。
 
さて、動作確認が取れた所で、(今のバックアップは中途半端なゴミなので)削除して戻しておこう。
# rm -rf /media/backup/current
# umount /media/backup
# lvremove /dev/vg-root/lv-root.snap

 
今回はココまで。
次回は snapshot の削除だ。

2016年3月24日木曜日

システムバックアップその8

お次は、バックアップ対象の領域のうち、LVMのlvolで構成されている領域に対して、スナップショットを作成する部分だ。
スナップショット、って何か?
簡単に書くと、「その瞬間のコピーイメージ」だ。(実際には、完全なコピーが作られるわけではない)
ある特定の領域に対して、バックアップ中にファイルの更新が入ると、バックアップデータが不整合を起こすことになる。そのため、スナップショット(その瞬間のコピーイメージ)を作成し、スナップショットからバックアップを取得するようにしたい。そのためのスナップショットだ。
まずはスクリプト。
例によって、sudo su - してからね。
# cd
# cd backup/bin
# vi 0040_create_snapshot
中身はこんな感じ
--中身ココから--
#!/bin/sh

BINDIR=`/usr/bin/dirname $0`
BINDIR=`/usr/bin/realpath ${BINDIR}`
ETCDIR=`/usr/bin/realpath ${BINDIR}/../etc`

. ${ETCDIR}/config

# Create Snapshot
for FSCONFIG in ${ETCDIR}/fsconfig/*
do
  USELVM=""
  VOLNAME=""
  SNAPSIZE=""

  . ${FSCONFIG}

  . ${BACKUPPOINT}/current/config/blkid/${VOLNAME}

  if [ x${USELVM} = "xYes" -a x${TYPE} != "xswap" ]
  then
    /bin/sync;/bin/sync;/bin/sync
    /sbin/lvcreate -L ${SNAPSIZE}  \
                   -n ${VOLNAME}.snap \
                   --snapshot ${ROOTVG}/${VOLNAME}
  fi
done

--中身ココまで--
前半は、いつも通りの変数定義。
for ループの内部で、${FSCONFIG} と blkid/${VOLNAME} を取り込んでいる。
${FSCONFIG} は事前に定義しておくファイルで、${VOLNAME}は前回までの 0030_create_config で作成したファイルだ。
そして if文 を使って「LVM領域か?」と「swap領域以外か?」という確認をしている。
LVM領域でかつswap領域じゃなかった場合に、if文が成立して、syncコマンドとlvcreateコマンドが実行される。(まぁぶっちゃけ、swap領域を排除する必要は無いんだが…)
ここの lvcreate コマンドが、LVMスナップショットを作成しているコマンドだ。
lvcreate の前までに使用している変数は以下の2つ。
  • ${USELVM}
  • ${TYPE}
一つ目の${USELVM}は前回までに説明してある。${FSCONFIG}の内部で定義しておく変数だ。
次の${TYPE}は、前回作成するようにした、blkid/${VOLNAME}の中で定義されている。
念のため、確認してみるといい。
# blkid -o export /dev/sda1
# blkid -o export /dev/vg-root/lv-root

TYPE=vfat や TYPE=ext4 という出力結果があるはずだ。
0030_create_config で出力するようにしておいたファイルシステム構成情報を、こちらの 0040_create_snapshot で早速利用している。
で、LVM領域&swap以外、という条件が成立した領域に対して、lvcreate を実行しているわけだが、その文がコレ
    /sbin/lvcreate -L ${SNAPSIZE}  \
                   -n ${VOLNAME}.snap \
                   --snapshot ${ROOTVG}/${VOLNAME}

引数として3つ出ている。
1つ目を飛ばして、2つ目、-n ${VOLNAME}.snap
これは、スナップショットボリューム(lvol)につけるlvol名だ。
既に存在する lvol からスナップショットを生成するので、名前を付けておく必要が有る。
ここではベースとなる(スナップショット元になる)lvolの名前に、.snap を付与した名称にしている。
次の --snapshot ${ROOTVG}/${VOLNAME} は、ベースとなる lvol の指定だ。
つまり、${ROOTVG}/${VOLNAME} で示される lvol から、スナップショット ${VOLNAME}.snap を作成する、というコマンドになる。
戻って1つ目の引数。
-L ${SNAPSIZE} は、スナップショットの差分保持領域サイズだ。
スナップショットの仕組みが分からないと、何言ってるのかサッパリ分からないだろう。
ざっくり書くと、スナップショットを作成してからも、ベースのlvolには書き込みが出来る。その間、スナップショットは「作成直後」の中身を保持している。当然、ベースlvolとスナップショットの中身は差異が生じる。
この差異を持っているのがスナップショットだ。
つまり、ベースlvolの中身を丸っとコピーしているわけではなく、差分だけ保持しておいて、あたかも新旧2面持っているかのように見せかけているわけだ。
この「差分を保持できる領域はどれぐらいの量にしますか?」っていうのを指定するオプションが -L ${SNAPSIZE} だ。
スナップショットを作成してから、スナップショットを削除するまで(今回はバックアップ目的でスナップショットを作成するので、バックアップ完了まで)の間に、どれぐらいの書き込みが発生するのか?というのを予想して指定する必要が有る。
が、はっきり言って正確にサイズを予想するのは無理だ。
だいたい指標としては、「元のlvolのサイズの10~20%」とか言われているが、それだって正確じゃない。
/usr のように、通常運用時はほとんど書き込みが発生しない領域もあれば、/var のように頻繁に書き込み・書き換えが発生する領域もある。
なので、必要となるサイズは「えいやっ!」で決めてしまい、運用していく中で変更していく、という形を取ることになるだろう。
ちなみに、スナップショットサイズは、同VGの空き領域(Free PE)から確保されるため、その分だけ VG に空き領域が必要だ。(今ならまだ40GB以上余っているはずだ)
さて、スナップショットとlvcreateコマンドがなんとなく分かってきたところで、使用している変数を確認してみよう。
使っているのは3つ。
  • ${SNAPSIZE}
  • ${VOLNAME}
  • ${ROOTVG}
だ。
${ROOTVG}は、既に ${ETCDIR}/config の内部で定義している。
また、${VOLNAME} も、${ETCDIR}/fsconfig の中の設定ファイルで定義済みだ。(前回定義しているはず。)
残りは ${SNAPSIZE} だ。
これをドコに定義するか?だけど、先ほども書いたように、領域の特性によって、スナップショットサイズは変わって来る。そのため、領域ごとに作成している設定ファイル(${ETCDIR}/fsconfig の中のファイル)に定義するのがスジというものだろう。
なお、sda1 と sda2 はLVM制御下には無いため、${SNAPSIZE}を定義する必要は無いが、他の設定ファイルを合わせるために、敢えて記述している。
これまでで、${ETCDIR}/fsconfig の下には、3つのファイルが存在しているはずだ。それぞれにファイルに以下の内容を追記しよう。
# vi ../etc/fsconfig/0010_sda1
--追記ココから--
#
# SNAPSIZE:
# If the area of interest was the LVM region, we want to create
# a LVM Snapshot.
# It will specify the capacity of the Snapshot area.
# ex.
#   SNAPSIZE=32M
#   SNAPSIZE=320M
# If you specify a larger size than the capacity of the backup target,
# it will be the same capacity as the size of the backup target.
#
SNAPSIZE=

--追記ココまで--
# vi ../etc/fsconfig/0020_sda2
--追記ココから--
#
# SNAPSIZE:
# If the area of interest was the LVM region, we want to create
# a LVM Snapshot.
# It will specify the capacity of the Snapshot area.
# ex.
#   SNAPSIZE=32M
#   SNAPSIZE=320M
# If you specify a larger size than the capacity of the backup target,
# it will be the same capacity as the size of the backup target.
#
SNAPSIZE=

--追記ココまで--
# vi ../etc/fsconfig/0030_lv-root
--追記ココから--
#
# SNAPSIZE:
# If the area of interest was the LVM region, we want to create
# a LVM Snapshot.
# It will specify the capacity of the Snapshot area.
# ex.
#   SNAPSIZE=32M
#   SNAPSIZE=320M
# If you specify a larger size than the capacity of the backup target,
# it will be the same capacity as the size of the backup target.
#
SNAPSIZE=320M

--追記ココまで--
lv-root はLVM領域でかつswap以外の領域のため、SNAPSIZE を指定しておいた。320MByteにしているが、この数字が多いのか少ないのかははっきり言って不明だ。運用していく中で調整していくしか無い。
さて、ココまで出来たらテスト実行だ。
今まで通り、0010から順番に実行してみよう。
いつも忘れるんだけど、ココでも実行パーミッションを付与しておくのを忘れないように。
# chmod +x 0040_create_snapshot
# ./0010_mount
# ./0020_mkdir
# ./0030_create_config
# ./0040_create_snapshot
0040_create_snapshot を実行した時に、「Logical volume "lv-root.snap" created.」と表示されてたら、スナップショットの作成に成功だ。
ちなみに、スナップショットを作成している状態で、同じ名称のスナップショットを作成しようとするとエラーになる。(このまま、もう一度 0040 を実行したらエラーになるはずだ。)
2度連続して実行しないように注意しよう。(次の実行前には、スナップショットは削除しておこう。)
とりあえず、本当にスナップショットが作成されたか確認。
# lvs
  LV                  VG      Attr       LSize   Pool Origin         Data%  Meta%  Move Log Cpy%Sync Convert
  lv-root             vg-root owi-aos---   7.63g                                
  lv-root.snap        vg-root swi-a-s--- 320.00m      lv-root        0.83       
(一部省略している)

lv-root と lv-root.snap は表示されていて、lv-root.snap の Pool Origin が lv-root になっているはずだ。また、lv-root.snap の Attr の先頭の文字が  s になっていると思う。
これによって、lv-root.snap は、「lv-root から作成されたスナップショット」ということが分かる。
気になるようなら、一度マウントして、中身が lv-root と同じか見てみたらいい。
# mount -o ro /dev/vg-root/lv-root.snap /mnt
# ls / /mnt

どうだろうか。/(lv-root)と同じようなファイルシステムになっているのが確認できただろうか。
後は、/dev/vg-root/lv-root.snap からファイルを一式バックアップすれば、lv-root のバックアップが完了する。
ファイルのバックアップ処理は次回以降に記載することとして、とりあえずテスト・確認のためのマウントを外して、スナップショットは削除しておこう。
合わせて、作成されたcurrentディレクトリの削除と、/media/backup のアンマウントも忘れないように。
# umount /mnt
# lvremove /dev/vg-root/lv-root.snap
(本当に削除していいか?と聞かれるので、名前が間違っていないことを確認して、 y で削除しよう。)
# rm -rf /media/backup/current
# umount /media/backup

これでスナップショットを作成するスクリプトは完成だ。
この次は、ファイル一式をバックアップする処理になる。
ちなみにこの先、
  • スナップショットを削除する処理
  • ファイルシステムメタデータ取得処理(実は不要)
  • バックアップディレクトリ(current)を年月日時分秒にリネーム
  • 古い世代のバックアップの削除
と流れていって、バックアップが完了する。
そして、これらのバックアップ処理を順番に実行していく親スクリプトを用意する予定だ。
リストアの方は、以下の3つ+親スクリプトの計4本を予定している。
  • HDD構成情報の反映(パーティション等作り直し)
  • ファイルシステム作成
  • ディレクトリ、ファイル書き戻し
まだまだ先は長い…。
2016/03/28追記
/media/backup/current ディレクトリの削除を忘れていたので、そのコマンドを追記。
2016/03/30追記
リストアの予定スクリプト、一覧が漏れていたので記入。

システムバックアップその7

0030_create_config の作成もあと僅か。
先日の作業に続けて、0030_create_config に以下の行を追記しよう。
--追記ココから--
for FSCONFIG in ${ETCDIR}/fsconfig/*
do
  USELVM=""
  VOLNAME=""
 
  . ${FSCONFIG}
 
  if [ x${USELVM} = "xNo" ]
  then
    /sbin/blkid -o export /dev/${VOLNAME} > ${BACKUPPOINT}/current/config/blkid/${VOLNAME}
  else
    /sbin/blkid -o export /dev/${ROOTVG}/${VOLNAME} > ${BACKUPPOINT}/current/config/blkid/${VOLNAME}
  fi
 
done

--追記ココまで--
今まで以上にグッとプログラムっぽくなった。(/sbin/blkid の2行、変な所で改行されているように見える部分があるかもしれないが、1行ずつだ。)
けど、中身は前回説明したforループと、if文、blkidコマンドの組み合わせに過ぎない。
変数の確認の前に、全体の流れを確認しておこう。
 
まずは forループ。
ループの回数に影響する部分が ${ETCDIR}/fsconfig/* となっている。
これは、${ETCDIR}/fsconfig の下のファイル一式、と考えてもらって構わない。
つまり、そのディレクトリに aファイルと bファイルが存在していた場合は、以下のようになる。
for FSCONFIG in ${ETCDIR}/fsconfig/a ${ETCDIR}/fsconfig/b
つまり、FSCONFIGという変数に、ファイル名が格納されるということになる。
 
次のポイントは、". ${FSCONFIG}"の部分か?
これは、「このスクリプトのこの部分(ドットがある部分)に、${FSCONFIG}のファイルの中身を差し込む」という意味になる。
先ほどの for文 と合わせて考えると、ループ1回目には、この部分に ${ETCDIR}/fsconfig/a の中身が差し込まれ、ループ2回目には ${ETCDIR}/fsconfig/b というファイルが差し込まれる、というわけだ。
この差し込まれるファイル、この後で使用している ${USELVM}という変数と、${VOLNAME}という変数が定義してある。
つまり、ループを回る都度、変数の値を変更されて実行される、という意味合いになる。
 
そして if文。
if文は、特に解説しなくてもいいかと思う。
if [ x${USELVM} = "xNo" ]
これは、${USELVM}がNoだった場合(頭に x を付与しているので、全体としては xNo だった場合、という条件になるけど…)、then以降を実行し、Noでは無かった場合、else以降を実行するという、いわゆる「条件分岐」だ。
 
ここまで見れば、${ETCDIR}/fsconfig/* のファイルは、「あ、マウントポイント(ファイルシステム)毎に定義ファイルを作って、その対象領域がLVM管理下だったら、${USELVM}変数にYesを、LVM管理外だったらNoを入れればいいんだな?」と予想出来ると思う。
 
もう一つの変数 ${VOLNAME} は、blkidコマンドで使用されている。
blkidコマンドは、ブロックデバイス(HDDパーティションとか)の属性値を出力するコマンドだ。特に -o export というオプションを付与すると、変数定義のような形で出力してくれる。
ブロックデバイスを指定する、ということは、${VOLNAME}はブロックデバイス名を指定すればイイ、ということが予想できると思う。
ズバリその通りで、sda1 とか lv-usr-local と指定している。
 
これによって、ブロックデバイスのUUIDやDEVNAME、ファイルシステムタイプが出力(保存)出来ることになる。
ちょうど、前回書けなかった 「4. 各ファイルシステムのファイルシステムフォーマット情報やuuid情報」 に相当する部分を出力することが出来るわけだ。
 
おっと忘れてはいけない。
これらを出力するためには、etc/fsconfig の下に、それぞれのブロックデバイス(バックアップ対象領域)の定義ファイルを作らないといけない。
いきなり全部作るのは結構大変なので、今は3つぐらいにしておこう。(最終的には全て作らないといけないのだが…)
まずは1つ目
# vi ../etc/fsconfig/0010_sda1
--ココから--
#
# USELVM:
# Area to be backed up or LVM region, simple partition of the flag.
# Specify a Yes in the case of LVM of LVOL area.
#
USELVM=No

#
# VOLNAME:
# You want to specify the volume device name of the target volume.
# For simple partition, sda1 and sda2 like.
# You can easily find LVM of LVOL area, specify the LVOL name (lvol1, etc.).
#
VOLNAME=sda1

--ココまで--
 
続いてsda2
# vi ../etc/fsconfig/0020_sda2
--ココから--
#
# USELVM:
# Area to be backed up or LVM region, simple partition of the flag.
# Specify a Yes in the case of LVM of LVOL area.
#
USELVM=No

#
# VOLNAME:
# You want to specify the volume device name of the target volume.
# For simple partition, sda1 and sda2 like.
# You can easily find LVM of LVOL area, specify the LVOL name (lvol1, etc.).
#
VOLNAME=sda2

--ココまで--
 
これまでの2つは、LVM配下ではない領域だった。今度はLVM配下の領域(lvol)を1つだけ作成しておこう。(あくまで、とりあえず、で。最後は必ず全部作る必要が有るよ。)
# vi ../etc/fsconfig/0030_lv-root
--ココから--
#
# USELVM:
# Area to be backed up or LVM region, simple partition of the flag.
# Specify a Yes in the case of LVM of LVOL area.
#
USELVM=Yes

#
# VOLNAME:
# You want to specify the volume device name of the target volume.
# For simple partition, sda1 and sda2 like.
# You can easily find LVM of LVOL area, specify the LVOL name (lvol1, etc.).
#
VOLNAME=lv-root

--ココまで--
 
ここまで出来たら、テストで動かしてみよう。
0010から順番に動かせばいいぞ。
その前に、実行パーミッションを付けておくのを忘れないように。
# chmod +x 0030_create_config
# ./0010_mount
# ./0020_mkdir
# ./0030_create_config
# ls /media/backup/current/config
# ls /media/backup/current/config/blkid
0030_create_configを実行したところで「Voume group .... successfully backed up.」と表示されたかもしれない。
vgcfgbackupを実行すると表示されるメッセージで、successfullyと出ていれば特に気にしなくていい。
無事にファイルが出来ているだろうか?config ディレクトリの下には、efibootやsfdisk.sda、vgcfgbackup.vg-root、vgs.pvid といったファイルが出来ているはずだ。
また、config/blkid ディレクトリの下には、sda1 とか sda2、lv-root というファイルが出来上がっているはず。
全てテキストファイルなので、中身を確認したかったらcat等で確認しておこう。(できれば、一通り中身を確認して、少なくとも空では無いことだけはチェックしておこう。)
 
ざっと、こんなもんだろうか。上手く説明出来ない部分も当然あるし、もっと良いやり方があるかもしれない。あくまで私が作った部分なので…ということにしておいてくれぃ。
 
今回はココまで。
次は、LVMのlvol領域のスナップショットを作成する部分だ。

2016年3月23日水曜日

システムバックアップその6

さて、バックアップを取得するための器は出来上がった。
今回は、バックアップ対象である内蔵ディスクの構成情報をバックアップしよう。
構成情報は、ざっと以下の内容を取っておく必要がある。
  1. efibootに必要なefibootmgr情報
    (今回の機器は、uEFIマシンのため、efi構成情報が必要だ。レガシーBIOSの人は、特に必要無いぞ)
  2. LVM構成情報
    1. vg構成
    2. pvのuuid等
  3. 内蔵ディスクのfdiskパーティション情報
    (MBRもしくはGPT。uEFIなので、GPTになるはずだ。)
  4. 各ファイルシステムのファイルシステムフォーマット情報やuuid情報
2.~4.に関しては、1エントリだけじゃなく、複数エントリが存在する可能性がある。
そのため、複数エントリに対応した作りにする必要がある。
そのあたりを考慮しながら、1つずつ作っていこう。
いつも通り、sudo su - で root にスイッチしてから作業を行おう。
# cd
# cd backup/bin
# vi 0030_create_config
まずは、以下の内容を作る。
--ココから--
#!/bin/sh
 
BINDIR=`/usr/bin/dirname $0`
BINDIR=`/usr/bin/realpath ${BINDIR}`
ETCDIR=`/usr/bin/realpath ${BINDIR}/../etc`
 
. ${ETCDIR}/config
/bin/efibootmgr -v > ${BACKUPPOINT}/current/config/efiboot

--ココまで--
最後の1行以外は、これまでのスクリプトと大した差がないので特に解説は不要だろう。
最後の efibootmgr コマンドが初出だ。
これは、現在のefibootの構成情報が出力されるコマンドだ。(uEFIにboot情報をに設定することも可能なコマンドだ。)
efibootmgr -v で、現在のefibootの詳細情報が出力されるので、それをファイルにリダイレクトし、保存している。
気になるようなら、実際に実行してみるといい。
# efibootmgr -v
BootCurrent: 0001
Timeout: 1 seconds
BootOrder: 0001,0005,0002,0000
Boot0000* Windows Boot Manager  VenHw(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)WINDOWS.........x...B.C.D.O.B.J.E.C.T.=.{.x.x.x.x.x.x.x.x.-.x.x.x.x.-.x.x.x.x.-.x.x.x.x.-.x.x.x.x.x.x.x.x.x.x.x.x.}....................
Boot0001* ubuntu        HD(1,GPT,xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,0x800,0xf3800)/File(\EFI\ubuntu\shimx64.efi)
Boot0002* LAN : IBA GE Slot 00C8 v1553  BBS(Network,,0x0)..BO
Boot0005* SATA : PORT 0 : INTEL SSDSC2BW120H6 : PART 0 : Boot Drive     BBS(HD,,0x0)..BO
こんな風に色々と出てくる。
現在のuEFIのブートエントリ一式と、「今ブートしているのは、そのエントリの何番か?」「ブート順」等の情報が出てきている。
これらを保存しておこう、というのが先ほどの行の目的だ。
ただ…実はリストアにはこの結果は用いていない。そのため「何かトラブった時の参考情報」として保存している程度の情報だ。
これが、「1.efibootに必要なefibootmgr情報」の取得に該当する部分だ。
 
続けて、0030_create_config に以下の1行を追記しよう。
--追記ココから--
/sbin/vgcfgbackup -f ${BACKUPPOINT}/current/config/vgcfgbackup.${ROOTVG} ${ROOTVG}

--追記ココまで--
vgcfgbackup というコマンド名からある程度予想が出来ると思うけど、vgの構成情報を取得しているコマンドだ。
「-f ファイル名」で、構成情報を保存するファイル名を指定している。
引数の最後に vg名を指定することで、指定した vg の構成情報が取得できる。
LVM制御下のバックアップ対象領域は、全て vg-root に属することにしているため、引数は対象は vg-root だが、敢えて変数を指定している。
まずは、どんな情報が取得出来るのか、自分の目で確認してみて欲しい。
# vgcfgbackup -f /tmp/vg-root vg-root
# cat /tmp/vg-root
(ここにズラズラと内容が出てくる)
# rm /tmp/vg-root
どうだろう?ホスト名やVG名、VGを構成しているPVの名前、各LVの名前やサイズ等が記録されているはずだ。
リストアする時には、このファイルを使えば、いちいちlvcreate等をしていく必要が無く、LVM構成情報はサクッと戻せる。
 
で、先ほどの1行に戻って欲しいのだが、変数として${ROOTVG}というのを使用している。これは初めて出てくる変数だ。この変数は、configファイルに定義しておこう。(configファイルは前回作成しているはずなので、追記しよう。)
# vi ../etc/config
--以下の内容を追記--
#
# ROOTVG:
# The name of the Volume Group that make up the OS.
# Only one can be specified.
#
ROOTVG=vg-root

--追記ココまで--
これで、「2.LVM構成情報 - 1.vg構成」を取得する部分が完了だ。
 
次は順番からしたら「2.LVM構成情報 - 2.PVのuuid等」になるんだけど、スクリプトを書いた時、ちょっと異なる順番で書いている。
そのため、次は「3.内蔵ディスクのfdiskパーティション情報」だ。
先ほどと同様に、0030_create_config に以下の内容を追記しよう。
--ココから--
for ROOTPV in ${ROOTPVS}
do
  /sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
done

--ココまで--
いきなりグッとプログラムっぽくなった。
ここで注目して欲しいのは以下の行。
/sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
sfdisk コマンドに --dump というオプションを付けて実行している。
その次の /dev/${ROOTPV} は、具体的には /dev/sda 等に置き換わると思って欲しい。
そして、リダイレクトを用いてファイルに書き出している。
そのため、実際にやっているコマンドは以下の通りだ。
sfdisk --dump /dev/sda
実際にやってもらうと分かるが、/dev/sda ディスクの fdiskパーティション情報を出力しているコマンドだ。(ファイルにリダイレクトしているので、最終的にファイルに書き出されるのだが)
単純にコレだけの処理なのに、わざわざ複雑にしている。
それは以下の理由による。
  • OS領域のバックアップとしては、vg-rootを構成する情報を取得しておく必要がある。
  • 今の構成は、vg-root を形作る PV は、 /dev/sda3 のみ。
  • ディスク領域が足りなくなって、別のディスク(/dev/sdb等)を増設し、vg-root に組み入れる可能性がある。
  • その場合、/dev/sda だけでなく、/dev/sdb の情報もバックアップしておかなければならない
  • バックアップスクリプトとしては、複数のディスク情報(fdisk情報)を取得出来るようにしておくべき
とまあ、そういうわけで少し複雑な作りになっている。
 
ここまで理解してもらったら、スクリプト部分の前に変数を確認しておこう。
今回のブロック、新たに出てきた変数は以下の2つだ。
  • ${ROOTPVS}
  • ${ROOTPV}
このうち、${ROOTPV}の方は、スクリプト内部で自動的に生成しているので、${ROOTPVS}の方が必要になってくる。
${ROOTPVS}についても、config ファイルに定義することになる。config ファイルに以下の内容を追記しよう。
# vi ../etc/config
--追記ココから--
#
# ROOTPVS:
# Device name of the PV that make up the ROOTVG.
# And not the disk partition name, to specify the physical disk name.
# Specifically, we want to specify the /dev/sda1 instead of /dev/sda.
# Because sometimes it is composed of a plurality of devices,
#  specified in the array.
# ex.
# ROOTPVS="sda sdb sdc sde"
#
ROOTPVS="sda"

--追記ココまで--
${ROOTPVS}には、ただ一つ"sda"という値を設定しているだけだ。
ただ、コメントにサンプルを載せている。これを読んでもらえば予想がつくと思うが、vg-root を構成するHDDが、/dev/sda と /dev/sdb の2つになっている場合は、"sda sdb"と指定してもらえばいい。
そういう風に、複数のHDDを指定できるように準備しておいた。
 
さて、スクリプトに戻ると…。
次に注目して欲しいのは、以下の行
for ROOTPV in ${ROOTPVS}
これは「for文」と呼ばれる繰り返しループ処理の制御文だ。
そして、次の do から done までが繰り返し実行される部分になる。
シェルスクリプト(sh系)の場合の for 文は、以下の様な書き方になる。
for 変数 in 値1 値2 値3 ...
do
  繰り返し実行される行
done
一回目の実行は、「値1」が「変数」に格納されて、「繰り返し実行される行」が実行される。
doneまで実行されたら、「値2」が「変数」に格納されて、また「繰り返し実行される行」が実行される。
これが、値Xが無くなるまで繰り返されるのだ。
 
それを踏まえて、もう一度スクリプトを見てみよう。
for ROOTPV in ${ROOTPVS}
先ほどのconfigファイルにより、${ROOTPVS}には"sda"が格納されているはずなので、実際には以下の行に置き換わる。
for ROOTPV in sda
つまり、ROOTPVという変数に、sda という値が格納されて、その下の do ~ done が実行されるわけだ。
で、doneまで行った後、次の変数が無いため、forループが終了する。
これが、${ROOTPVS}が"sda sdb"だった場合はどうなるか?
for ROOTPV in sda sdb
という風に置き換わるため、ROOTPVという変数に、sda という値が格納されて do ~ done が実行され、その後 ROOTPV という変数の値が sdb という値に置き換わって do ~ done が実行される、という具合に動く。
 
ということは、${ROOTPVS}の中身が "sda" のみだった場合は、結局以下のような実行文になる。
for ROOTPV in ${ROOTPVS}
do
  /sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
done
↓
for ROOTPV in sda
do
  /sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
done
↓
ROOTPV=sda
/sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
↓
/sbin/sfdisk --dump /dev/sda > ${BACKUPPOINT}/current/config/sfdisk.sda

もし、${ROOTPVS}の中身が "sda sdb" だった場合は、以下のようになる。
for ROOTPV in ${ROOTPVS}
do
  /sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
done
↓
for ROOTPV in sda sdb
do
  /sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
done
↓
ROOTPV=sda
/sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
ROOTPV=sdb
/sbin/sfdisk --dump /dev/${ROOTPV} > ${BACKUPPOINT}/current/config/sfdisk.${ROOTPV}
↓
/sbin/sfdisk --dump /dev/sda > ${BACKUPPOINT}/current/config/sfdisk.sda
/sbin/sfdisk --dump /dev/sdb > ${BACKUPPOINT}/current/config/sfdisk.sdb

vg-root を構成するディスクが何本になっても、config ファイル内の ${ROOTPVS}の値を書き換えることで対応が可能になった。
この対応のために、少し複雑な作りになっている。
ちなみに、do ~ done の間は1行しか無いが、当然複数行のコマンドを入れることも可能だ。
これで、「3. 内蔵ディスクのfdiskパーティション情報」の情報取得が出来るようになった。
 
続いて「2. LVM構成情報 - 2. pvのuuid情報等」だ。
こちらは1行で実現している。0030_create_config に以下の1行を追記しよう。
--追記ココから--
/sbin/vgs --unquoted --noheadings --nameprefixes
--options pv_name,pv_uuid,vg_name ${ROOTVG} >
 ${BACKUPPOINT}/current/config/vgs.pvid

--追記ココまで--
vgsコマンドを、何やら複雑なオプション付きで実行し、ファイルにリダイレクトしている。
(行が長すぎて3行に渡っているが、実際には1行で書いて欲しい)
リダイレクトしている部分を除くと、以下の様なコマンドだ、ということが分かる。(${ROOTVG}は、既に中身が vg-root ということが分かっているはずなので、敢えて置き換えて表記する)
vgs --unquoted --noheadings --nameprefixes --options pv_name,pv_uuid,vg_name vg-root
これも、参照コマンドなので、よく分からなかったら実行してしまおう。
# vgs --unquoted --noheadings --nameprefixes --options pv_name,pv_uuid,vg_name vg-root
  LVM2_PV_NAME=/dev/sda3 LVM2_PV_UUID=XXXXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXXXX LVM2_VG_NAME=vg-root
何やらデータが出てきた。
これ、よく見てみると分かるのだが、vg-rootを構成するpvの「デバイス名」と「UUID」だ。
今の構成では1行しか出力されていないが、vg-root が複数の pv で構成されている場合は、複数行表示されるぞ。
しかもよく見ると、変数宣言っぽい形で出力されている。リストア時は、これを上手く変数として取り込むことが出来れば、有効活用出来そうだ。
これを取り込むのは、リストアスクリプトの方で解説することにしよう。
駆け足だが、これで「2. LVM構成情報 - 2. pvのuuid情報等」が取得できるようになった。
 
次は最後、「4. 各ファイルシステムのファイルシステムフォーマット情報やuuid情報」だ。
こちら、少し複雑なので、次回にしよう。
(いい加減、ちょっと疲れてきたので…)

2016年3月21日月曜日

システムバックアップその5

お次はバックアップが格納されるディレクトリの作成。
格納されるファイルは、大きく以下の種類になる。
  • アーカイブファイル本体(各種ファイルのアーカイブ)
  • バックアップ/リストアに用いるスクリプト
  • バックアップ/リストアに用いる設定ファイル
  • ディスク構成情報やLVM構成情報等の構成ファイル
これらを複数世代保持出来る必要がある。
今回は、以下のように設計した。
  • /backup/media
    • current
      • bin
        バックアップ/リストアスクリプト
      • etc
        バックアップ/リストア設定ファイル
      • config
        リストアに用いる構成情報ファイル
        • tune2fs
          ファイルシステムのパラメータ情報(取得はするけど参考情報)
        • blkid
          各ファイルシステムのblkidコマンドの結果(リストア時に使用)
      • archive
        バックアップファイル本体
      • snapshot
        バックアップ対象領域を一時的にマウントするマウントポイント
    • YYYYMMDDhhmmss
      過去のバックアップ
      • bin
      • etc
      • config
        • tune2fs
        • blkid
      • archive
      • snapshot
    • YYYYMMDDhhmmss
      ​過去のバックアップ
      • bin
      • etc
      • config
        • tune2fs
        • blkid
      • archive
      • snapshot
バックアップ処理の開始時に、currentディレクトリとその下のディレクトリを作成し、バックアップ処理が完了した時点で、currentディレクトリをその時の年月日時分秒(14桁)にリネームして保持する、という形だ。
ディレクトリ作成時に、既にcurrentディレクトリが存在している場合は、別途バックアップ処理が稼働中か、前回のバックアップ処理が失敗している可能性がある。
その場合はエラーとして処理を中断している。
 
また、binディレクトリとetcディレクトリは、バックアップ時に使用するディレクトリをそっくりそのままコピーしてしまうことを想定している。
 
以上のことを踏まえて、スクリプトを作成しよう。ファイル名は 0020_mkdir だ。
(前回と同様、sudo su - を実行して、rootユーザにスイッチしてから実行しよう。)
# cd
# cd backup/bin
# vi 0020_mkdir
--ファイルの中身はココから--
#!/bin/sh
 
BINDIR=`/usr/bin/dirname $0`
BINDIR=`/usr/bin/realpath ${BINDIR}`
ETCDIR=`/usr/bin/realpath ${BINDIR}/../etc`
 
. ${ETCDIR}/config
 
if [ -e ${BACKUPPOINT}/current ]
then
  exit 1
fi
/bin/mkdir ${BACKUPPOINT}/current
/bin/mkdir ${BACKUPPOINT}/current/archive
/bin/mkdir ${BACKUPPOINT}/current/config
/bin/mkdir ${BACKUPPOINT}/current/config/tune2fs
/bin/mkdir ${BACKUPPOINT}/current/config/blkid
/bin/mkdir ${BACKUPPOINT}/current/snapshot
/bin/cp -pr ${BINDIR} ${BACKUPPOINT}/current/
/bin/cp -pr ${ETCDIR} ${BACKUPPOINT}/current/

--ココまで--
ファイル内部で使用している変数は、前回と同じだ。そのため、特に解説はしない。
 
作成できたら、これまた前回と同様、実行パーミッションを付与しておこう。
# chmod +x 0020_mkdir
それではテストしてみよう。
テストは、前回作成した0010と組み合わせて行う。
# ./0010_mount
# ./0020_mkdir
# echo $?
(0と表示されるはず)
# ls /media/backup
# ls -R /media/backup/current
(作成されたディレクトリが表示される)
どうだろう、ちゃんと作成できているのが確認できただろうか?
 
では、既にcurrentディレクトリが存在する状態では?
# ./0020_mkdir
# echo $?
(1が表示されるかな?)
echo $? の結果が異なる(ゼロではない)ことが確認できただろうか?
一通り確認できたら、currentディレクトリを削除して、/media/backup はアンマウントしておこう。
# rm -rf /media/backup/current
# umount /media/backup
これで、ディレクトリ作成スクリプトは完成だ。
ここまでは簡単で、次回から少し難しくなる。頑張って進めよう。
 
というわけで今回は以上。
 
2016/03/23追記
スクリプト先頭の、${BINDIR}と${ETCDIR}を生成する部分のコマンドを、cdとpwdの組み合わせではなく、realpathコマンドに置き換えた。
こっちの方がソレっぽくてカッコイイ。(意味不明)

2016年3月20日日曜日

システムバックアップその4

さて、ようやくバックアップ本体の方の話に。
その前に、ファイルの構成を考えないといけない。
ファイル構成は大きく…
  • スクリプト本体
    • バックアップスクリプト
    • リストアスクリプト
  • 設定ファイル
    • バックアップ/リストア全体に関わる設定
    • 個々の領域(ファイルシステム領域)に関する設定
に分けられる。
設定ファイルとスクリプトが同じディレクトリに配置されると、結構ごちゃごちゃしてメンテナンスしにくくなるため、ディレクトリを分ける。
Unix系OSの流儀に従うのならば、スクリプト本体は実行形式のファイルのため bin ディレクトリに、設定ファイルは etc ディレクトリに配置される。
今回は、rootのホームディレクトリ(/root)に、backupというディレクトリを作成し、その下に bin と etc の両ディレクトリを作成することにする。
とその前に、今後はrootでの作業が多く発生するので、都度sudoでroot権限を借り受けるよりは、rootにスイッチしておこう。
$ sudo su -
(ここで聞かれるパスワードはrootのパスワードではなく、自分自身のアカウントのパスワードだ)
rootにスイッチ出来たら、ディレクトリを作成していこう。
# cd
# mkdir backup
# mkdir backup/bin
# mkdir backup/etc
# ls -l backup
合計 8
drwxr-xr-x 2 root root 4096  3月 14 23:03 bin
drwxr-xr-x 3 root root 4096  3月 10 22:37 etc
また、個々のファイルシステムに関する設定は、同じような内容のファイルを各領域毎に作成することになるため、それは etc の下に別のディレクトリを用意して、そちらに配置するようにしよう。(今回は、fsconfig というディレクトリにした)
# mkdir backup/etc/fsconfig
ではスクリプトと設定ファイルを作っていこう。
スクリプトは、大きなファイルを作るのではなく、小さな単位で作成して、それを組み合わせて行く方が、品質も高く、作るのもテストするのも楽だ。これはスクリプトに限った話ではなく、コンパイル言語でも同様だ。
そのため、小さな単位で作っていく。
ただ、スクリプトの数が増えるのも管理がしにくくなるので、スクリプトファイル名はその機能を表す名前にする。
また、呼び出す順番を把握しやすくしたいため、ファイル名の先頭に、4桁の数字をつける。
これは、あくまで自分の好みのやり方だ。
スクリプト数が全体でいくつぐらいになるのか、最初のうちはまったくわからない。ま、100や200なんて数にはならないはずだ。
とは言え、作っていく過程で、「3番と4番の間に、もう一つ別のスクリプト差し込みたい」という要求も出てくるはずなので、ファイル名のナンバリングは10置きにしている。
というわけで一つ目。
一つ目は、バックアップ用領域のマウントだ。
以下の手順で作成しよう。
# cd backup/bin
# vi 0010_mount
--ファイルの中身はココから--
#!/bin/sh
 
BINDIR=`/usr/bin/dirname $0`
BINDIR=`/usr/bin/realpath ${BINDIR}`
ETCDIR=`/usr/bin/realpath ${BINDIR}/../etc`
 
. ${ETCDIR}/config
 
/bin/mountpoint -q ${BACKUPPOINT}
RET=$?
 
if [ ${RET} -ne 0 ]
then
  /bin/mount ${BACKUPPOINT}
else
  /bin/mount -o remount,rw ${BACKUPPOINT}
fi

--ココまで--
作成し終わったら、実行パーミッションを付与しておこう
# chmod +x 0010_mount
このスクリプトには、3つの変数が出てくる。
3つというのは${BINDIR}、${ETCDIR}と${BACKUPPOINT}だ。
  • ${BINDIR}
    スクリプト本体が格納されているディレクトリ。これはスクリプト自身で生成している。
  • ${ETCDIR}
    設定ファイルが入っているディレクトリ。${BINDIR}から想定して生成している。
  • ${BACKUPPOINT}
    こちらがスクリプト内部では生成していない変数だ。
    これは、バックアップ領域がマウントされるマウントポイント(これまでの記述で、/media/backupにマウントされる前提だ)で、利用者の都合に合わせて変更できるように、設定ファイルに記載するようにした。
マウントポイントは、バックアップ/リストア全体に関わる内容なので、etcディレクトリ直下にある設定ファイル(ファイル名については今まで記載していなかったが、configという名前にした。安直すぎるけど。)に記載する形にしよう。
configファイルを以下の手順で作成する。(今は bin ディレクトリにいると思うので、相対パスで ../etc/config を編集する。)
# vi ../etc/config
--中身はこんな感じ--
#
# BACKUPPOINT:
# Mount point of the backup area.
# At the start of the backup process, it mounts the backup disk here.
# The /etc/fstab, that it should be specified with noauto option.
#
BACKUPPOINT=/media/backup
--中身ココまで--
書き上がったらテストしてみよう。
/media/backup がマウントされていない状態で実行してみる。
# mount | grep /media/backup
# ./0010_mount
# mount | grep /media/backup
(ズラズラとマウントされた情報が出てくるはず)
この時に、マウントされているオプションに rw という情報も含まれていることを確認しよう。(read / write モードってことね。)
では、既にマウントされているこの状態で、もう一度実行してみたらどうなるか?
# ./0010_mount
# mount | grep /media/backup
(マウント情報に変化はない、はず…)
次に、一旦アンマウントして、リードオンリーモードでマウントしておき、同じようにスクリプトを実行してみよう。
# umount /media/backup
# mount -o ro /media/backup
# mount | grep /media/backup
(先ほどは rw だったオプションが、ro になっているはず)
# ./0010_mount
# mount | grep /media/backup
(再び、rw モードでマウントされた)
何らかの理由で、バックアップ領域をマウントして確認することもあり、マウントしたままでバックアップを実行してしまうこともあり得るので、そういった自体を想定した作りにしてある。
 
これでファイルシステムのマウント部分は完成だ。アンマウントしておこう。
# umount /media/backup
また、作業が終わったら、root権限で不用意にコマンド実行をしないように、rootから抜けておこう。
# exit
次回は、バックアップ領域にデータ格納用ディレクトリや、構成情報格納用ディレクトリを作成する部分を作る。
 
というわけで、今回はココまで。
 
2016/03/23追記
スクリプトの先頭部分の、${BINDIR}変数、${ETCDIR}変数を生成する部分を、cdとpwdの組み合わせから、realpathコマンドを使うように変更。
こっちの方がカッコイイと思うから。

2016年3月14日月曜日

システムバックアップその3

前回に引き続いて、システムバックアップの初期設定。
今回は、/etc/fstab のエントリにある、/boot/efi のマウント情報を書き換えるところだ。
多分、当該のエントリは以下のようになっていると思う。
UUID=XXXX-XXXX  /boot/efi       vfat    umask=0077      0       1
つまり、ディスクパーティション(ファイルシステム)に付与されているUUIDを指定して、/boot/efi にマウントしている形になっている。
 
実はコレが都合が悪い。
システムリストアは、ディスクパーティションから全て作りなおすことを想定しているのだが、/etc/fstab がこのままだと、パーティション・ファイルシステムを作り直す時に、同じUUIDを設定しないといけない。
ところが、OSのインストールメディアからブートした環境では、vfat領域にフォーマットするときに、UUIDを指定することが出来ない。自動でUUIDが振られ、後から変更することが出来ないのだ。
そうなると、リストアの度に、新しく振られたUUIDを確認して、そのUUIDで /etc/fstab を書き換える、という手間が発生する。
 
というわけで、この手間を省くために UUID指定ではないマウント形式で、/etc/fstab を書き換えてしまおう、というのが今回の主旨。
 
やり方としては多分以下の2つ。
  1. デバイスファイル名(/dev/sda1)を指定する
  2. 任意のボリュームラベル(fatlabel)を付与して、そのラベルでマウント指定する
正直、どちらでもいいとは思うんだが、今回は(2)を採用する。
 
まずは、当該領域にラベルを付与する必要がある。
ラベルに関しては、アルファベットや記号、11文字以下という制約があり、それに従って付与する必要がある。
ここでは、/boot/efi にマウントする領域、ということで、/BOOT/EFI というラベルにした。
vfat領域にラベルを付与するコマンドは、そのものズバリの fatlabel というコマンドだ。
ただ、fatlabelを確認、付与するためには、一度アンマウントしておいた方が安全のようだ。
具体的には以下のコマンドになる。
$ sudo umount /boot/efi
$ sudo fatlabel /dev/sda1
(まだラベルが未設定なので、「NO NAME」と表示されると思う)
$ sudo fatlabel /dev/sda1 /BOOT/EFI
$ sudo fatlabel /dev/sda1
(設定したラベル「/BOOT/EFI」が表示されるはず)
これで、/dev/sda1 に /BOOT/EFI というラベルが付与された。
付与したラベルでマウントできるか確認してみよう。
$ mount | grep /boot/efi
$ sudo mount LABEL=/BOOT/EFI /boot/efi
$ mount | grep /boot/efi
(正しくマウントされたのなら、エントリが表示されるはずだ)
後は、/etc/fstab を、UUIDではなく LABELでマウントするように修正しよう。
$ sudo vi /etc/fstab
UUID=XXXX-XXXX  /boot/efi       vfat    umask=0077      0       1
↓(以下のように書き換え)
LABEL=/BOOT/EFI /boot/efi      vfat    umask=0077      0       1
書き換えが終わったら、正しくマウントできるか再確認だ。
$ sudo umount /boot/efi
$ mount | grep /boot/efi
$ sudo mount -a
$ mount | grep /boot/efi
エラーなくマウント出来れば、次回以降の再起動でも問題なくマウントされるはず。
 
これで初期設定は終わりかな?次回以降、ようやくスクリプト本体に入れるはず。
というところで今日はおしまい。

2016年3月12日土曜日

システムバックアップその2

というわけで、システムバックアップ用スクリプトを作成していく。
実は既に作成が完了していて、リストアも出来ている。
ただ、いきなり全てを書くのはタイヘンなので、少しずつ書いていくことにする。
 
まず初期設定。
バックアップ取得先はNASのCIFS領域を想定している。
Ubuntuの場合、最小構成のインストールでは、CIFS領域をマウントすることが出来ないため、必要なパッケージを導入しておく必要がある。
ブログの最初の方でも書いておいたが、cifsマウントするためには、cifs-utilsというパッケージを導入すればいい。
$ sudo apt-get update
$ sudo apt-get install cifs-utils
これでcifsをマウントできるはずだ。
(一回目の sudo では、自分自身のパスワードを確認されるはずなので、パスワードを入力しよう)
 
試しにマウント出来るか確認する。
マウントポイントは、バックアップ領域用に先に作成しておこう。
$ sudo mkdir -p /media/backup
そして、マウントコマンド
$ sudo mount -t cifs -o guest \
//NASのIPアドレス/NAS内部のバックアップ用ディレクトリ \
/media/backup
$ mount
正常にマウントできれば、最後のmountコマンドで、/media/backup も出力されるはずだ。
(sudo mount の行が長すぎて右に切れてしまったので、複数行に分けて書いた。)
(\ で改行しているので、そのまま入力してくれればいいはずだ。)
(NAS側で、ゲストアカウントが有効になっていることが前提だ。この辺りは、使用しているNASのマニュアルで確認してくれい。)
 
マウント出来たら、読み書きできるかも確認しておこう。
$ ls -l /media/backup
$ sudo touch /media/backup/hoge
$ ls -l /media/backup
$ sudo rm /media/backup/hoge
$ ls -l /media/backup
 
読み書き確認が出来たら、アンマウントしておく。
$ sudo umount /media/backup
$ mount
 
この領域は、普段からマウントしているわけではなく、必要に応じてマウントすることを考えている。そのため、バックアップ処理の開始時点でマウントしたい。
ただ、スクリプト内部にマウントのオプションを細々と書くのはイヤなので、初めから /etc/fstab に書いておく。
$ sudo vi /etc/fstab
fstab に記載する内容は以下のようになる。
//NASのIP/NAS内部のバックアップ用ディレクトリ 
/media/backup cifs noauto,guest,sfu 0 0
(行が長すぎたので複数行に分けたが、実際には1行で書いて欲しい)
(sfuの記述はなくてもいい。この後のマウント確認でマウント出来ないようなら、sfuの記載を消して再確認しよう。)
 
/etc/fstab への記載が終わったら、もう一度マウント確認してみよう。
$ sudo mount /media/backup
$ mount
$ sudo umount /media/backup
$ mount
 
また、/etc/fstab に記載しているけど、noauto オプション(boot時に自動マウントしない)が有効かどうかを、OS再起動して確認もしてみよう。
$ sudo shutdown -r now
(再起動するので、しばらく待って再度ログイン)
$ mount
どうだろうか?自動マウントされてない状態になっているだろうか?
 
これで、バックアップスクリプト内部では、
mount /media/backup
umount /media/backup
と書くだけで、マウント・アンマウント出来るようになる。
 
今回はココまで。
次回は、/etc/fstab の内部で、 /boot/efi が UUID指定のマウントになっているので、それを変更することについて記載する。
 

2016年3月10日木曜日

システムバックアップその1

とりあえずsshでログイン出来る状態になったので、システムバックアップ(OSバックアップ)を取れるようにしておきたい。
Ubuntuに限らず、Linux系のOSには、システムバックアップを取る機能が無くて、他社製品とかを使うことが多いと思う。特にビジネスで使用する場合は、他社製品に頼らざるを得ないのでは?
ここでは、簡単なコマンドの組み合わせで、システムバックアップが取れるような環境を作りたいと思う。
で、まずは方針(ポリシー)を決めておく必要が有る。
(方針決めないと、ぐっちゃぐちゃになるからね)
 
バックアップ・リストアの方針
  • 何のためのバックアップか?
    • SSD/HDDの故障でのフルリストア
    • 作業ミスでファイルが消えてしまって稼働しなくなった場合のフルリストア
  • バックアップ対象ディスク
    • 内蔵HDD(SSD)とする。
    • 今後利用する予定のiSCSIや、USBストレージは対象外。
    • 但し、OS領域のVG拡張に伴って、同じVG領域(今回の構成だと、vg-root)にディスクを追加した場合は、その追加ディスクもバックアップ対象とする。
  • バックアップ内容
    • HDDのパーティション情報(今回だとgptパーティション)と、レイアウト。(パーティションが幾つあって、それぞれどれだけの容量なのか?)
    • パーティションのUUID
    • LVM未使用&ext4の領域は、ファイルシステムのUUID
    • LVM構成情報(LVが何の名前で幾つあって、それぞれ容量がどれだけか?)
    • ファイルシステムのフォーマット(ext4か、vfatか?)
    • 各ファイルシステムに格納されているファイル
  • バックアップレベル
    • フルバックアップとする。(差分バックアップはしない)
  • バックアップ保持世代数
    • 複数世代を保持出来るようにする
  • 実装
    • できるだけスクリプトにして、手間をかけないようにする
  • バックアップサイクル
    • 任意のタイミングで実行できるようにする。cronで定期実行も考慮。
  • バックアップ取得先
    • NASに取ることを想定。NFSではなくCIFS(Windows用NASの領域)
  • リストア単位
    • 原則、HDD(SSD)全体のリストア。パーティション情報とか全て作りなおす形。
    • ​バックアップ取得時の状態に戻すことを想定する。
  • リストア手順
    • OSのインストールメディア(isoイメージを焼いたCD-R等)でブートし、レスキューモードからリストアを実行する。
      (つまり、リストアで使えるコマンドは、OSイメージからレスキューモードに入った時に使えるコマンドのみ。)
ざっくりとこんな感じだ。
今後、この方針に沿って、スクリプトを組んでいく。
 

2016年3月8日火曜日

sshで接続@Agent認証その2

前回で、Pagent用の秘密鍵は作成した。
 
次は、そのPagentを利用する流れだけど…。実はそんなに難しくない。
作成した秘密鍵をダブルクリックすると、パスフレーズを入力するダイアログが出てくるはずだ。そこに、秘密鍵のパスフレーズを入れればいい。


「OK」を押したら、ダイアログが消えて、何も無かったかのような状態になる。
が、タスクトレイ上に、pagentのアイコンが出来ているはずだ。


このアイコンを右クリックして出てくるメニューから、「View Keys」を選んでみよう。そうすると、このPagentが取り込んでいる鍵の情報が出てくる。
これが、先ほどダブルクリックした鍵だ。


Pagent側はこれで準備が整った。
 
今度はteraterm側だ。
teraterm側は、sshログインの認証方式を選択するダイアログで、以下の様な状態にすればいい。


ユーザ名を入力し、認証方式を「Pagentを使う」にすればOKだ。
これで、パスフレーズを入力することなく、鍵認証でログインすることが出来る。
何回ログインしても、パスフレーズを入力しなくていい。
これがAgent認証だ。
本当にPagentを使用しているか疑問に思えたら、タスクトレイからPagentを終了して、再度teratermからログインしてみたらいい。Pagentが無い、と怒られるはずだ。
 
ただし、手元のWindows端末を再起動する等で、常駐しているPagentが終了した場合は、再度秘密鍵をダブルクリックして、Pagentに鍵を読み込ませる必要がある。
この辺りは身体で覚えよう。
 
また、agent方式を使うと、agent転送という機能も利用できる。
上の認証方式を入力する画面でも「エージェント転送する」というチェックボックスがあるので、そういう名前の機能が存在することはわかると思う。
このエージェント転送、複数台のサーバを経由するようなログインには非常に便利なのだが、今はまだそういう機能を使うメリットが無い。いずれ使う機会もあると思うので、その時に書こうと思う。
 

2016年3月7日月曜日

sshで接続@Agent認証その1

前回で、Ubuntuに対してteratermを使って、パスワード認証と公開鍵認証を用いたsshログインが出来るところまで試した。
ただ、公開鍵認証を用いても、秘密鍵にパスフレーズを付与しているので、毎回ログインのたびにパスフレーズを入力しなければならず、結構手間だ。
今回は、agentを用いて、パスフレーズを一回だけ入力すれば済むような方向で進めていく。
 
agentって?
クライアント側で秘密鍵を保持しておいてくれて、クライアントソフト(teraterm等)と連携して鍵認証を代行してくれるプログラム。
通常の鍵認証だとこんな感じだけど…


エージェントを使うと、このような感じになる。


teratermで、同じサーバに複数のログインセッションを作るなんていうことはよくある話だが、その都度秘密鍵を読み込んでいたら、毎回パスフレーズを入力しなければならない。結構な手間だ。
そこで、事前にSSH Agentソフトウェアを立ち上げ、そちらで秘密鍵を読み込んでおく。(ここで1回、パスフレーズを入力することになる。)
teraterm等は秘密鍵を読み込まずに、秘密鍵が必要な時にSSH Agentに処理を任せる、というような形にすることで、最初の一回(SSH Agentで秘密鍵を読み込む時)、パスフレーズを入力するだけで済むようになる。
 
Ubuntuをクライアントにしている場合は、OpenSSHに含まれているssh-agentというツールで実現が可能だが、クライアントがWindowsの場合、OS標準にはこのようなツールは存在しない。(そもそも、sshクライアントすら存在しないわけだが…)
agent機能は、フリーソフトで幾つか実現されているようなのだが、今回は PuTTY というフリーソフトに含まれている Pagent というツールを使うことにする。
これ、すごく有名らしく、調べればすぐに情報が出てくると思う。
 
PuTTY は簡単にダウンロード、インストール出来るので、先にインストールしておいて欲しい。
 
で、以前 teraterm で作成した秘密鍵を、PuTTY の Pagent に読み込ませればOKなのだが…、実は Pagent は、teraterm で作成した秘密鍵の書式に対応していない。
そのため、書式を変換する必要がある。
変換はそんなに難しいことではない。PuTTYに変換ツールも含まれているからだ。
PuTTYのインストールが完了していたら、PuTTYgenというツールがインストールされているはずだ。スタートメニューから探して、実行してみて欲しい。
以下の様な味気ない画面が出てくるはずだ。


メニューバーに「Conversions」というのがあるのに気付くと思う。
それらしい名前だが、そのものズバリだ。こちらを選ぶと、Import keyというメニューが出てくる。これをクリックする。


Import keyをクリックすると、お約束のファイル指定ダイアログが出てくるので、以前teratermで作成した鍵のうち、秘密鍵(.pubじゃない方)を指定しよう。
パスフレーズを聞いてくると思うので、秘密鍵に付与したパスフレーズを入力する。


パスフレーズを入力して「OK」を押したら、PuTTYgenの画面が少し派手(?)になったと思う。


あとは「Save Private key」を押して、PuTTY用の秘密鍵をローカルに保存すればいい。(拡張子は.ppkになる)
これで、Pagent用の秘密鍵が出来上がりだ。
 
次は、作成した鍵を PuTTY の Pagent に読み込ませて…なんだけど、今日はここまで。また次回。

2016年3月6日日曜日

sshで接続@鍵認証

IPアドレスの固定化が出来たので、遠隔からログイン出来るようにしておきたい。
とは言っても、既にssh-serverは導入済みのはずなので、クライアント側の設定を行えばいい。
クライアント側は、Windowsならteratermやputty等を使えば、すぐにログイン出来るようになると思う。
ここでは、teratermとputtyの中のpagentを使い、agent認証でログインできるようにする。
が、まずは鍵認証が出来るように。

teratermのインストールが終わったら、まずは鍵ペアを作成しよう。
teratermの起動直後の「新しい接続」ダイアログは「キャンセル」。
メニューバーの「設定」から、「SSH鍵生成」を選択しよう。
鍵生成のダイアログが出てくる。

鍵の種類は色々あるけど、汎用性があってそこそこ使えるのはRSA。鍵長(ビット数)も、2048でとりあえずは十分だろう。これを長くすると、暗号強度は高まるが、処理にかかる負担も大きいので、大きくしたとしても4096までにしておいた方がいいだろう。
鍵の種類とビット数を決めたら「生成」ボタンを押す。
しばらくすると、ダイアログが以下のように変わるはずだ。


ある程度慣れている人だと、パスフレーズ無しの方が使い勝手がいい、ということで、パスフレーズ無しにしている人も多いと思うけど、agentを使うことも考えているので、ここはパスフレーズを入力しておく。


後は、「公開鍵の保存」と「秘密鍵の保存」の両方のボタンを押して、それぞれをファイルに保存しておこう。
(既に作っている鍵もあるので、ファイル名はそれぞれ id_rsa_usekey.pub と id_rsa_usekey にしてある)



ここまで出来たら、ログイン確認をする。
デフォルトでは確か、パスワード認証も鍵認証も両方許可されていたと思うので、ここでは両方試してみる。

まずはパスワード認証。
メニューバー「ファイル」から「新しい接続」を選ぶ。


ホストの欄に、前回設定したIPアドレスを、サービスはSSH(ポートも22番)で、SSHバージョンはSSH2にしておこう。(既にSSH1では接続出来ないはず。)


これで「OK」を押すと、以下のダイアログに変わるはずだ。
(初回接続時は、ホスト鍵保存していいか?というダイアログが出てくる。OKを押しておこう。)
下の方のラジオボタンは「プレインパスワードを使う」にしておこう。
ユーザ名にUbuntu上のアカウント名、パスフレーズに、そのアカウントのパスワードを入力し、「OK」を押すと、ログイン出来るはずだ。


通常のパスワードでログイン出来るのが確認できたら、次は先ほど作成した鍵を用いて認証出来るか試してみよう。

鍵認証を使用するためには、サーバ側(Ubuntu側)に、先ほど作成した鍵のうち、公開鍵を登録しておく必要がある。
ホームディレクトリの下に、sshというディレクトリを作成し、その中で authorized_keys というファイルに、鍵を追記していく形になる。
ログインしたままのteratermがあるはずなので、以下の手順でディレクトリを作成しておこう。

mkdir .ssh
chmod 755 .ssh

自分自身以外が書き込み出来ないよう、パーミッションを変更するのも合わせてやっておく。

先ほど作成した鍵(.pub となっている方)をサーバへアップロードする必要があるのだが、teratermなら簡単に出来る。
ログインしたままのteratermで、メニューバー「ファイル」から「SSH SCP」を選択する。


以下のダイアログが表示されるので、Fromに先ほどの鍵を、Toには、~/.ssh/id_rsa_usekey.pub と入力し、「Send」ボタンを押そう。


そうすれば、先ほど作成したファイルが、サーバ上に格納されているはずだ。

ls ~/.ssh/id_rsa_usekey.pub
/home/******/.ssh/id_rsa_usekey.pub

後は、このファイルを authorized_keys に追記すればいい。

cat ~/.ssh/id_rsa_usekey.pub >> ~/.ssh/authorized_keys

さて、鍵認証が出来るかどうか、もう一枚teratermを立ち上げて試してみよう。
ログイン先やサービスは先程と同じようにして、認証画面を以下のようにしよう。
ラジオボタンは「RSA/DSA/ECDSA...を使う」とし、秘密鍵を先ほど作成した鍵(.pubではない方)を選択する。
画面上部の「ユーザ名」は、サーバ側のアカウント、パスフレーズは、秘密鍵を作成した時に入力したパスフレーズだ。(OS側で設定したパスワードではない)


これで「OK」を押すと、すんなりログイン出来るようになるはずだ。

ただ、これではログインするたびに毎回パスフレーズを入力しなければいけない。
メンドウだ。
なので、次回はagent(pagent)を用いて、パスフレーズ入力を一回限りにするようにしてみたい。