2014年9月7日日曜日

SoftLayerに冗長化NFSサーバを(その6)

(その4)で紹介した Rescue モードの使い心地を改良します。

現時点での Rescue モードの不満点は以下の通りです。

・物理マシンの場合、再起動に時間がかかる
・SoftLayer の Rescue モードは、使えるコマンドが古い(CentOS 5.8)
・SoftLayer の Rescue モードは、コマンド名や使い方が標準と異なるものがある
・SoftLayer の Rescue モードは、物理マシンの場合、起動直前までコンソールが接続不能
・SoftLayer の Rescue モードに頼るとベンダーロックと騒ぎ出す人が出てくるかも
 (最後の手段として利用できるのは非常にありがたいです。物理マシンの場合、PXE ブートという手もありますが、仮想マシンの場合はネットワーク経由を考えると他に手がないです。コンソールこそが最後の手段ですがインストーラ DVD イメージから起動できる環境が保証されているわけではないですし、起動シーケンスの途中からしか繋がらなかったりしますし…)
・(その4) Rescue モードは、/boot/grub/grub.conf 編集でモードを切替(戻すのが面倒)
・もっとシンプルにできるのではないか、改良の余地あり、という気がしてならない

なんといっても、最後の問題が一番深刻です(笑)。放っておくわけにはいかないのです。
AWS 人気の理由の一つは、出来ないこと、制約が多すぎて、技術者に対して挑戦状が叩きつけられ、何とか他の人が思いつかなかった方法を駆使してでも解決させる、という楽しみがあるからだと思っています。いわばオモチャですが、幾つになっても、楽しいものは楽しいです。技術者以外からするとハタ迷惑な話だとは思っていますが、応戦せざるを得ないのです。技術者以外の人が AWS を選択する理由が私にはよく理解できません。唯一の理由は、パブリッククラウドのデファクトスタンダードになっているということでしかないです。市場というものはそんなものだろう、とは思っています。
その点 SoftLayer は、オンプレからクラウドに移行してもできてしかるべきだと思っていたことが基本的には全てできますし、意表を突かれるような仕様も AWS と比べればまったくと言っていいほどないです。先日、私にとって4個目のパブリッククラウドを触る仕事が始まりましたが、Oracle RAC が動かない、Oracle ライセンスがままならない、という点でおそらく…。なぜかこの手の仕事が私のところに回ってきます。Azure は触ったことが無いのですが、何の因果か、現時点までで、ギャラなしで触っているのは SoftLayer だけです。いわば、技術者の良心として選んだ結果とも言えます。ギャラが出ていないので、かなり自由にブログを書けている、という面もあります。さすがに自腹だと検証する気など全くおきないので、Catalyst プログラム万歳です。来年の4月まで、状況が許される範囲で、頑張っていきます。

話を戻します。kexec を利用して、(その4)で紹介した Rescue モードへの切り替えを改善します。

kexec は、OS のコアダンプを取得するために利用されることが多いですが、単体の機能としては、カーネルイメージ等をメモリに読み込んでおき、そのカーネルに実行を移せます。通常、OS の再起動を実行する場合、OS を停止状態に持っていき、サーバをリセットしますが、リセットすると BIOS が呼び出され、初期化処理が動き出します。サーバの場合この処理に数分かかることが多いのですが、この処理が不要な場合には、kexec を利用して再起動させることでスキップできます。
以下のように再起動する例がよく紹介されています。


sudo kexec -l /boot/vmlinuz-$(uname -r) --append="$(cat /proc/cmdline)" --initrd=/boot/initramfs-$(uname -r).img
sudo reboot


この処理は、現在動いているカーネルを、起動時に指定されたオプションを再現して再起動しています。RHEL の停止処理では、kexec でカーネルをロードしている場合、内部で kexec -e を呼び出してくれます。

私的には、再起動といえば、/boot/grub/grub.conf の設定を読み込んでほしいと思います。例えば以下のような感じです。このコマンドで再起動すると、物理マシンでは20秒弱で再起動が完了します。


cat << 'EOF' | sudo tee /usr/local/sbin/reboot_quick
#!/bin/bash
[ "$1" = "noreboot" ] && shift && NOREBOOT=yes
if [ ! -e /proc/xen ]; then
  LINE=$(grep ^default= /boot/grub/grub.conf | sed 's/default=//')
  KVER=$(grep -v ^# /boot/grub/grub.conf | grep vmlinuz- | sed 's/^.*vmlinuz-\([^ ]*\) .*$/\1/' | head -$((LINE+1)) | tail -1)
  CMDLINE="$(grep -v ^# /boot/grub/grub.conf | grep vmlinuz- | sed 's/^.*vmlinuz-\([^ ]* \)\(.*\)$/\2/' | head -$((LINE+1)) | tail -1) $@"
  /sbin/kexec -l /boot/vmlinuz-$KVER --initrd=/boot/initramfs-$KVER.img --command-line="$CMDLINE"
fi
[ "$NOREBOOT" = "yes" ] || /sbin/reboot
EOF
sudo chmod 755 /usr/local/sbin/reboot_quick

sudo /usr/local/sbin/reboot_quick


kexec を利用して再起動後に Rescue モードへ移行することもできます。
現在のプライベート IP アドレス(冗長化している場合は bond0、していない場合は eth0 の IP アドレス)、ゲートウェイ IP アドレスを設定して起動します。物理マシンの場合はジャンボフレームを有効化しています。
仮想マシンの場合は、準仮想化だと kexec がうまく動かないので /boot/grub/grub.conf を編集しています。残念ながら、Rescue モードにて、編集して元の設定に戻す必要があります。
仮想マシンの場合の改良はあきらめます。
Windows の仮想マシン(完全仮想化)を発注して Linux 化すれば動くのかもしれませんが、さすがに検証する気はありません。AWS が仮想マシンを準仮想化から完全仮想化に移行している渦中なので、SoftLayer も近いうちにそうなるのかもしれません。その時に機会があればやってみることとします。


sudo mkdir /rescue
cat << 'EOF' | sudo tee /rescue/reboot
#!/bin/bash
if [ -e /proc/xen ]; then
  sed -i -e 's/^\(default=.*\)$/##rescue##\1\ndefault='"$(($(sudo cat /boot/grub/grub.conf | grep -v ^# | tr '\n' ',' | sed -e 's/title/\ntitle/g' | grep ^title | awk '/ rescue / {print NR}')-1))/" /boot/grub/grub.conf && reboot
else
  kexec -l /boot/vmlinuz --initrd=/boot/initrd.img --command-line="rescue repo=http://mirrors.service.networklayer.com/centos/6.5/os/x86_64/ lang=en_US keymap=jp106 selinux=0 sshd=1 nomount ksdevice=eth0 ip=$(ifconfig $(ifconfig bond0 > /dev/null 2>&1 && echo bond0 || echo eth0) | grep inet | awk '{print $2}' | awk -F: '{print $2}') netmask=255.255.255.192 gateway=$(if route -n | grep -q '^10\.0\.0\.0'; then route -n | grep '^10\.0\.0\.0'; else route -n | grep '^0\.0\.0\.0'; fi | awk '{print $2}') dns=$(grep ^nameserver /etc/resolv.conf | head -1 | awk '{print $2}') mtu=9000 $@" && reboot
fi
EOF
sudo chmod 755 /rescue/reboot
[ -e /proc/xen ] && sudo sed -i -e 's/^installonly_limit=.*$/installonly_limit=2/' /etc/yum.conf


使い方は非常に簡単で、次のように実行すると1分ほどで Rescue モードに ssh 接続可能となります。


sudo /rescue/reboot


物理マシンの場合には、このコマンドは引数を複数指定可能です。そのままカーネル起動オプションとして渡します。仮想マシンの場合は引数を無視します。
カーネル起動オプション指定では、解釈できないものは無視されます。つまり、カーネルや anaconda (インストーラ)が無視できるものであれば自由に指定できます。受け取る側では、/proc/cmdline から取り出して解釈します。バックアップ先となる NFS サーバの IP アドレスを渡すなど、いろいろと活用範囲があります。使い道は検証目的くらいでしかなさそうですが先の reboot_quick コマンドも同様の仕様にしておきました。

例えば、Rescue モードからは以下のようにして取り出します。


for i in $(cat /proc/cmdline)
do
  echo $i | grep -q = || continue
  echo $i | grep -q -v ^= || continue
  eval $i
  echo $i
done


kexec を利用する以外にも、もう少し改良の余地があります。
現時点では、物理マシンの場合、[repo]/images/install.img をロードする前に、[repo]/images/updates.img[repo]/images/product.img がないかを探すために無意味に待っています。これを待たないようにすれば、20秒弱で ssh 接続可能となるはずです。ただし、これに対応しようとすると自分でリポジトリを用意しなければなりません。今回は見送ります。

また、残念ながらボンディングには対応できていません(CentOS 6.4 から anaconda が対応しているようですが、lacp_rate=fast との設定がどうしてもうまくいきません。lacp_rate=slow となってしまいます)。ボンディング設定を入れなくても問題なく通信はできます。必須の設定とまでは言えませんが、SoftLayer の Rescue モードが対応しているので、こちらの Rescue モードでも対応する必要があります(笑)。
本来の / パーティションをマウント後に利用できるコマンドとして用意しておきます。


cat << 'EOF' | tee /rescue/mk_bond0
#!/bin/sh
ifconfig eth2 > /dev/null 2>&1 || exit 0
ifconfig bond0 > /dev/null 2>&1 && exit 0
IP=$(ifconfig eth0 | grep inet | awk '{print $2}' | awk -F: '{print $2}')
GATEWAY=$(if route -n | grep -q '^10\.0\.0\.0'; then route -n | grep '^10\.0\.0\.0'; else route -n | grep '^0\.0\.0\.0'; fi | awk '{print $2}')
modprobe bonding
#echo +bond0 > /sys/class/net/bonding_masters
ifconfig bond0 down
echo 4 > /sys/class/net/bond0/bonding/mode
echo 100 > /sys/class/net/bond0/bonding/miimon
echo fast > /sys/class/net/bond0/bonding/lacp_rate
echo 1 > /sys/class/net/bond0/bonding/xmit_hash_policy
ifconfig eth0 down; \
ifconfig eth2 down; \
ifconfig bond0 $IP netmask 255.255.255.192 up mtu 9000; \
echo +eth0 > /sys/class/net/bond0/bonding/slaves; \
echo +eth2 > /sys/class/net/bond0/bonding/slaves; \
route add -net 0.0.0.0/0 gw $GATEWAY
EOF
chmod 755 /rescue/mk_bond0


NIC のオフロード機能もオフにしておきたいです。Windows はモロにバグっていてオフにするのがおまじないと化していますが、Linux でも影響を受けることがあります。転ばぬ先の杖として、このような最適化は無効化しておく方が安心です。バグっている部分があるのがわかっているはずなのに、デフォルトで有効化されていることが不可解です。OS ベンダーとしては、ハードウェアとして機能がサポートされていればデフォルトで有効化するので、ハードウェアベンダはバグのないドライバを提供してね、という理屈は分からなくもないですが、サーバは安定性(予期できる速度で動く)の方を重視してほしいところです。


cat << 'EOF' | tee /rescue/mk_offload_off
for i in eth0 eth1 eth2 eth3 bond0 bond1
do
  ifconfig $i > /dev/null 2>&1 || continue
  echo "[$i]"
  for j in rx tx sg tso ufo gso gro lro rxvlan txvlan ntuple rxhash
  do
    ethtool --offload $i $j off 2> /dev/null
  done
  ethtool --show-offload $i
done
EOF
chmod 755 /rescue/mk_offload_off


Rescue モードで起動直後にグローバル IP アドレスが割り当てられているインターフェイスをダウンさせたり、sshd を停止したりしていましたが、こちらもスクリプト化しておきます。


cat << 'EOF' | tee /rescue/mk_secure
#!/bin/sh
ifconfig bond1 down > /dev/null 2>&1
ifconfig eth1 down > /dev/null 2>&1
ifconfig eth3 down > /dev/null 2>&1
/etc/init.d/sshd stop > /dev/null 2>&1
kill -KILL $(ps -ef | grep [s]shd | grep anaconda | awk '{print $2}') > /dev/null 2>&1
EOF
chmod 755 /rescue/mk_secure


残りのマウント作業もスクリプト化します。NFS サーバの IP アドレスを第1引数に指定することにします。


cat << 'EOF' | tee /rescue/mount
#!/bin/sh
mkdir /backup
mount -t nfs $1:/backup /backup
[ -e /proc/xen ] && DEV=xvda || DEV=sda
mount /dev/${DEV}1 /mnt/sysimage/boot
#mount -t proc /proc /mnt/sysimage/proc
#mount -t sysfs /sys /mnt/sysimage/sys
#mount --bind /dev /mnt/sysimage/dev
EOF
chmod 755 /rescue/mount

cat << 'EOF' | tee /rescue/unmount
#!/bin/sh
umount /mnt/sysimage/dev
umount /mnt/sysimage/sys
umount /mnt/sysimage/proc
umount /mnt/sysimage/boot
[ -e /proc/xen ] && DEV=xvda || DEV=sda
mount -o ro,remount /dev/${DEV}2 /mnt/sysimage/
umount /backup
EOF
chmod 755 /rescue/unmount


システムバックアップの手順の一部をスクリプトにまとめます。とりあえず版でしかないので手抜きでファイル名を決め打ちしています。


cat << 'EOF' | tee /rescue/backup
#!/bin/sh
/mnt/sysimage/rescue/mk_secure
/mnt/sysimage/rescue/mk_bond0
/mnt/sysimage/rescue/mk_offload_off
/mnt/sysimage/rescue/mount $1
[ -e /proc/xen ] && sed -i -e '/^default=/d' -e 's/^##rescue##default=/default=/' /mnt/sysimage/boot/grub/grub.conf
cd /mnt/sysimage/
tar czvf /backup/backup11.boot.tgz boot
umount /mnt/sysimage/boot/
tar czvf /backup/backup11.root.tgz .
cd
/mnt/sysimage/rescue/unmount
EOF
chmod 755 /rescue/backup


ここまで準備しておくと、システムバックアップの手順は以下のようになります。


sudo /rescue/reboot

ssh -o "StrictHostKeyChecking no" root@backup11

mkdir /mnt/sysimage
[ -e /proc/xen ] && DEV=xvda || DEV=sda
mount /dev/${DEV}2 /mnt/sysimage/

/mnt/sysimage/rescue/backup 10.110.88.62

umount /mnt/sysimage
reboot


※ ここでの reboot についても、kexec を利用して再起動時間の短縮化が可能です。chroot してゴニョゴニョします。

かなりすっきりしてきたのですが、バックアップをする、という点に特化して考えるなら、コマンド1つで実行したい、と発想するのが通常の感覚だと思います。何とか考え、やってみることにしましょう。

OS を自動インストールするためのカラクリとして、インストーラの anaconda にはキックスタートという機能があります。ここまでは anaconda の Rescue 機能を利用する前提で話を進めてきたのですが、キックスタートを使えば、システムバックアップはコマンド1つで実行できるのではないか、と考えています。キックスタートを利用する為には、どこかにキックスタートファイルを配置する必要があるのですが、幸いなことに、今回は NFS サーバがあるので、こちらに配置することができます。

今回のクラスタは heartbeat サービスを自動起動にしていないので完全自動化は無理ですが、この点を除けば、cron 登録して定期自動バックアップも可能だろうと思っています。SoftLayer の API を呼び出したりしないので、オンプレへの移植も簡単だと思います。
hearbeat サービスを自動起動するように仕様を変えてもいいですが、このクラスタについては、現状のままであれば、システムバックアップを定期的にとるメリットがほとんどないので、仕様変更しません。パッチ適用前に念のためバックアップを取得する、というくらいだと思っています。パッチ適用を自動的に定期的に実施する、という場合には、改めて考えるべきことが増えそうな気がしています。

とりあえず、できそうな気がしてきた(頭の中では完成している)ので、次回をお待ちください。

0 件のコメント:

コメントを投稿