2009年6月30日火曜日

スナップショット使用してMySQLの動作している仮想サーバをバックアップ

以前、IPnutsでvserverを使った仮想サーバのバックアップについて、ディスクの構成を中心にして紹介しました。
仮想サーバのバックアップ(その1)
仮想サーバのバックアップ(その2)
仮想サーバのバックアップ(その3)

この時は、まだLVMを使ってなかったので、スナップショットは使わずにバックアップをとっていました。

その後、ディスクが故障したためミラーリングしたディスク上でLVMを作成し、使用するように変更しました。

ミラー上にLVMを作る

また、他のサーバも移行してきました。

Pylonsの仮想環境を仮想マシンに移行する
Pylonsの仮想環境を仮想マシンに移行する(2)

しばらく、バックアップは以前のままでしたが、これだと、バックアップのときのダウンタイムが長くなります。そこで、せっかくLVMにしているのでスナップショットを利用して、ダウンタイムがほとんどなくなるようにスクリプトを書き直しました。

vserverの仮想サーバが2台分動作しています。非効率ですが、他のサーバから単純に移行したものなのでMySQLのデータベースもそれぞれの仮想サーバで稼働しています。

以下はバックアップのスクリプトです。

#!/bin/sh
MNTSNAP=/mnt/var_snap
VARSNAP=lvvar_snap
VARSNAPDEV=/dev/vg/$VARSNAP
VARDEV=/dev/vg/lvvar

vsqllock(){
vserver $1 exec mysql $2 -e "flush tables with read lock;"
}

vsqlunlock(){
vserver $1 exec mysql $2 -e "unlock tables;"
}

if [ -d $MNTSNAP ] ;then
mountpoint $MNTSNAP && umount $MOUNTSNAP
else
mkdir $MNTSNAP
fi

lvremove -f $VARSNAPDEV
vsqllock vserver1 database1
vsqllock vserver2 database2
sync
sync
lvcreate -s -L 1g -n $VARSNAP $VARDEV
vsqlunlock vserver1 database1
vsqlunlock vserver2 database2

if mount -o ro $VARSNAPDEV $MNTSNAP ;then
nice rsync -av --delete $MNTSNAP/lib/vservers/vserver1 /var2/lib/vservers/
nice rsync -av --delete $MNTSNAP/lib/vservers/vserver2 /var2/lib/vservers/
umount $MNTSNAP
else
echo "### mount failed ###"
echo "### mount -o ro,nouuid $VARSNAPDEV $MNTSNAP"
echo "### ------------ ###"
fi

lvremove -f $VARSNAPDEV


ネットワークから更新されるデータはデータベースのテーブルだけなので、仮想サーバを落とさずにMySQLのコマンドでテーブルが更新されないようにして、スナップショットをとります。これだと、ダウンタイムはほとんど起こりません。

mysqlのコマンドは、スクリプトの中ではvsqllock,vsqlunlockのなかで行っていますが、1行だけなので、vserverのexecコマンドでホスト側から直接仮想サーバーのmysqlコマンドを実行しています。

参考
http://labs.unoh.net/2006/09/lvm_xfs_mysql.html
http://dev.mysql.com/doc/refman/5.1/ja/flush.html

スナップショットは仮想サーバー側からとれないので、.sql文のなかでsystemコマンドを使ってスナップショットをとることはできません。

LVMでスナップショットをとったあと、すぐにロックをはずし、正常稼働状態にすることができます。その後スナップショットをマウントしてゆっくりバックアップをとることができます。

2009年6月15日月曜日

Pylonsの仮想環境を仮想マシンに移行する(2)

前回の続き

mysqlの移行
データベースはファイルをコピー
/etc/mysql/debian.cnf のパスワードもコピー

apacheの移行
/# apt-get install libapache2-mod-wsgi
wsgiの設定ファイルをコピー
環境に合わせてディレクトリの作成や設定ファイルを変更


なぜか、wsgiファイルにアプリケーションへのpythonのパスを足さないとエラーになった。
同じ環境のはずなのだが、何か違うのだろう

-- 動作するようになった
データのあまり無いサービスなので仮想マシンの環境は800MB程度。サーバのディスクが7GBあるので環境を作り直してかなり節約になった。

pound より nginx がよさそうなのでIPnutsのnginxのパッケージを作って、リバースプロクシの設定をした。



従来のからある仮想マシンのサービスはSNATで動作させていたが、これもnginx経由になった。

2009年6月13日土曜日

Pylonsの仮想環境を仮想マシンに移行する

Pylonsで動作しているサービスがあり、これのPythonの仮想環境をvserverの仮想マシンに移行する。

仮想マシンを新規作成
インターフェースはdummy0
# modprobe dummy
でできる。
仮想マシン名は vm1とすると

# vserver vm1 build --hostname vm1 -m debootstrap --interface dummy0:192.168.xx.yy/24 -- -d lenny -- --arch i386


時間が少しだけかかるのでコーヒーを買いにお買い物...
-----
これで仮想マシンは出来上がり

仮想マシンの環境整備

仮想マシンを起動して仮想環境に入る。

# vserver vm1 start
Starting enhanced syslogd: rsyslogd.
simplegw.sesame.local# vserver vm1 enter

sshサーバをいれて、/etc/ssh/sshd.config のListenAddress を入れておく

パッケージをインストールしてpythonの仮想環境を作る
vm1:/#apt-get install locales python apache2 mysql-server python-setuptools

vm1:/#easy_install virtualenv
...
vm1:/#mkdir -p /usr/local/python/BASELINE
vm1:/# virtualenv --no-site-packages /usr/local/python/BASELINE
New python executable in /usr/local/python/BASELINE/bin/python
Installing setuptools............done.

ユーザーを作ってから、ユーザで環境をつくる

$ mkdir pythonenv
$ virtualenv --no-site-packages pythonenv/MYAPP
New python executable in pythonenv/MYAPP/bin/python
Installing setuptools............done.

アクティベートしてpylonsをインストール
$ . pythonenv/MYAPP/bin/activate
(MYAPP)$ easy_install pytlons

これから下はアプリケーションが依存しているのでインストール

(MYAPP)$ easy_install authkit
(MYAPP)$ easy_install easy_install -U sqlalchemy

sqlのマイグレード関係
(MYAPP)$ easy_install sqlalchemy_migrate
(MYAPP)$ easy_install mysql-python

htmlを解析するツールも使っていたようなので、コンパイルする環境をインストールする
# apt-get install gcc
# apt-get install python-dev libxml2-dev libxslt1-devzlib1g-dev
# apt-get install libxml2 libxslt1 zlib1g

ユーザーで仮想環境をアクティベートして
(MYAPP)$ easy_install lxml

アプリケーションの移行
#gitのマスターが廃止予定のサーバにあるのでサーバの仮想マシンに移す。
#作業はファイルのコピーだけ。


gitをインストール

# apt-get install git-core

(MYAPP)$ mkdir pylons-public
ディレクトリを作って、gitのリモートサイトを登録してpull
(MYAPP)$ mkdir pylons-public/XXX
(MYAPP)$ cd pylons-public/XXX
(MYAPP)$ git init
(MYAPP)$ git remote add shared user@server:path_to_git
(MYAPP)$ git pull shared master
これを必要なディレクトリで行う

普通にコピーして持ってきてもOK
---つづく

2009年6月12日金曜日

サーバを復旧したディスク上で動かす

しばらく、バックアップ先のディスクの仮想マシンを動かしていたのを、新しく準備してバックアップになっていたミラーディスク上の仮想マシンで起動し直した。

4.3a3で現在稼働中。

仮想マシン以外の、openvpn(ユーザデータベース),ssl(openvpnの証明書関係),dnsmasq(tftpデータ)は再起動のシャットダウン時に、USBに保存されているはずなので、

# lrp_data-load -f

でデータを復旧。確認のため、/var2(バックアップ元)とdiffをかけてOK

マシンはサーバ入れ替えでパワーアップさせているので、他のサーバで動作している仮想マシンを移行予定

2009年6月11日木曜日

DRBD7からDRBD8.3.1への移行テスト(4)

XML差分ファイルの設定

セカンダリ側を立ち上げるが、その前にマスターとセカンダリの設定が同期がとれていないといけない。

IPnuts4.1ベータ版から、設定ファイルの差分を指定して立ち上げることが出きるようになっている。

/system/sysdiff.conf.sample と /system/sysdiff.conf.lvs_sampleのサンプルがある

/system/sysdiff.conf.lvs_sampleは lvsのセカンダリを作るときのサンプル。今回はこれに当たる。


## sysdiff.conf sample for lvs
##
## Change IP Address of port 2
#set item = { \
"xpath":"/SYSTEM/NETWORK/IP_LIST/IP[@port='2']/@ip", \
"value":"192.168.0.11" \
}
#silent $sysdiff_update.append($item)
##
## HOSTNAME
##
#set item = { \
"xpath":"/SYSTEM/HOST/HOSTNAME", \
"value":"gw2" \
}
#silent $sysdiff_update.append($item)
## VRRP setting
## -- CAUTION:
## -- Change vrid for your system.
##
## VRRP bigger prio has high priority.
##
#set item = { \
"xpath":"/SYSTEM/NETWORK/VRRP_INSTANCE_LIST/VRRP_INSTANCE[@vrid='50']/@
priority", \
"value":"50" \
}
#silent $sysdiff_update.append($item)
##
## VRRP initial state MATER|BACKUP
##
#set item = { \
"xpath":"/SYSTEM/NETWORK/VRRP_INSTANCE_LIST/VRRP_INSTANCE[@vrid='50']/@
state", \
"value":"BACKUP" \
}
#silent $sysdiff_update.append($item)



以下だけ説明する

## Change IP Address of port 2
#set item = { \
"xpath":"/SYSTEM/NETWORK/IP_LIST/IP[@port='2']/@ip", \
"value":"192.168.0.11" \
}
#silent $sysdiff_update.append($item)

PythonのテンプレートユーティティであるCheetahのテンプレートファイルの書式になっている
## #* *#はコメントになる
#はCheetahのコマンド 
それで 上記の例は xupdateに渡すサンプルで IPnutsの設定ファイルは/etc/config/sysconfig.xmlにあるXMLファイルで、それをxpathを指定して該当する値を変更する。
これだとport 2番のIPアドレスを変更する。

設定に変更がある時は、セカンダリ側では同じように設定を変更するのではなく、マスターのconfig.lrpをブートディスクにコピーしてセカンダリ側を作ることができる。
ただし、起動パッケージのリストの/system/lrp.lstを変更した時はこれもコピーする必要がある。

今回はマスター側のブートディスクからconfig.lrpと/system/lrp.lstをコピーしてセカンダリ側を立ち上げる。sysdiff.confのカスタマイズは環境によるが、IPアドレスとホストを設定すればよいと思われる。

セカンダリの起動とDRBDの同期

起動はできて、差分の設定もされているが。DRBDのディスクの同期が始まらない

error receiving ReportState, l: 4!
とか
Split-Brain detected, dropping connection!
のエラーが出る

これには次のようなコマンドを実行する。
r0はリソース名
セカンダリ側で

drbdadm secondary r0
drbdadm -- --discard-my-data connect

プライマリ側で

drbdadm connect

が..... .... プライマリ側のディスクが壊れている ???


めげずにそのままスレーブを再起動

スレーブ側は、何事もなく、drbd,keepalivedともに起動し

drbdadm role all にで Secondary/Primaryになった。

プライマリ側は、ディスクが壊れているようなので、同期を再開しようとしたときにエラーだしまくり。

ご参考まで

Jun 11 13:45:57 db1 kernel: hdb: dma_intr: error=0x40 <3>{ UncorrectableError }, LBAsect=65406887, sector=65406864
Jun 11 13:45:57 db1 kernel: ide: failed opcode was: unknown
Jun 11 13:45:57 db1 kernel: end_request: I/O error, dev hdb, sector 65406864
Jun 11 13:45:57 db1 kernel: drbd0: Local IO failed. Passing error on...
Jun 11 13:45:58 db1 kernel: hdb: dma_intr: status=0x51 { DriveReady SeekComplete Error }
Jun 11 13:45:58 db1 kernel: hdb: dma_intr: error=0x40 <3>{ UncorrectableError }, LBAsect=65406887, sector=65406872
Jun 11 13:45:58 db1 kernel: ide: failed opcode was: unknown
Jun 11 13:45:58 db1 kernel: end_request: I/O error, dev hdb, sector 65406872
Jun 11 13:45:58 db1 kernel: drbd0: Local IO failed. Passing error on...
Jun 11 13:46:00 db1 kernel: hdb: dma_intr: status=0x51 { DriveReady SeekComplete Error }
Jun 11 13:46:00 db1 kernel: hdb: dma_intr: error=0x40 <3>{ UncorrectableError }, LBAsect=65406887, sector=65406880
Jun 11 13:46:00 db1 kernel: ide: failed opcode was: unknown
Jun 11 13:46:00 db1 kernel: end_request: I/O error, dev hdb, sector 65406880
Jun 11 13:46:00 db1 kernel: drbd0: Local IO failed. Passing error on...
Jun 11 13:46:00 db1 kernel: drbd0: Sending NegRSDReply. sector 394072s.
Jun 11 13:46:00 db1 kernel: drbd0: Sending NegRSDReply. sector 394080s.
Jun 11 13:46:00 db1 kernel: drbd0: Sending NegRSDReply. sector 394088s.
Jun 11 13:46:00 db1 kernel: drbd0: Sending NegRSDReply. sector 394096s.
Jun 11 13:46:00 db1 kernel: drbd0: Sending NegRSDReply. sector 394104s.
Jun 11 13:49:00 db1 kernel: drbd0: Resync done (total 257 sec; paused 0 sec; 7140 K/sec)
Jun 11 13:49:00 db1 kernel: drbd0: 57 failed blocks
Jun 11 13:49:00 db1 kernel: drbd0: conn( SyncSource -> Connected )
Jun 11 13:49:00 db1 kernel: drbd0: cs:Connected rs_left=57 > rs_total=0 (rs_failed 0)



データは修復する必要はないし、ディスクもない、モチベーションもないから以上で移行テスト終了。

DRBD7からDRBD8.3.1への移行テスト(3)

DRBDのディスクのアップグレードは終了したので、マスター側を立ち上げてIPnuts4.3a3(testing)への環境整備をする。

そのまえに、IPnuts4.1b(ベータ版での機能をおさらい:というか今頃初公開かな)

自動起動スクリプトでディスク切り替え時にサービスの起動も制御する

自動起動の抑制
keepalivedのフックでサービスを制御するので、起動時に勝手に立ち上がらないようにする。
/etc/no_boot_rc

exim4
mysql
dhisd
nfs-kernel-server
bind9
apache2

これは、rc.lrpの設定ファイルになっていて/etc/init.d/rcSがこのファイルを参照する用になっている。

WebadminでLVSの設定(内部的にはkeepalivedの設定)を見ると

マスター時 実行コマンド/etc/keepalived/ha_start
バックアップ時 実行コマンド/etc/keepalived/ha_stop
フォルト時 実行コマンド/etc/keepalived/ha_stop

になっている。ha_start ha_stopは自作のshなのでパッケージには含まれてはいない。
ha_startの最後の方に
APPS="`cat /etc/keepalived/apps.list`"
for app in $APPS
do
if [ $? -eq 0 ] ;then
/etc/init.d/$app start
else
$LOGGER "fail to start $app"
fi
done
となっていて apps.listのサービスを起動している。
/etc/keepalived/appslistの中身はこれ
exim4
mysql
dhisd
nfs-kernel-server
bind9
apache2
4.3ではIPnutsだけで動作するのは nfs-kernel-server、bind9のふたつ。ほかはサポートする予定は無くて動作させる場合は仮想マシン(vserver)で動作させる。

よってexim,mysql,dhisd,apache2は起動パッケージのリスト(/system/lrp.lst)、no_boot_rc ,apps.listから消す。
(/etc/no_boot_rcはapps.listへのリンクになってる場合もある)

起動パッケージのリスト(/system/lrp.lst)からは他に起動時に読み込めなかった/var/lib/lrpkg/packages_fialedに記録されているパッケージも消す。

not found: - hotplug
not found: - dhcpcd
not found: - hotplug
not found: - less
not found: - xdelta
not found: - cipe
not found: - minihttp
not found: - license
not found: - passwd
minihttp,licenseは除外

設定保存して、再起動、動作確認

動かない。
keepalivedが起動すると、ha_startが動き、マウントするのだがディスクが古いことになっているからプライマリディスクにできないといって怒られる。

Jun 11 07:53:48 db1 kernel: drbd0: State change failed: Refusing to be Primary without at least one UpToDate disk
Jun 11 07:53:48 db1 kernel: drbd0: state = { cs:WFConnection ro:Secondary/Unknown ds:Outdated/DUnknown r--- }
Jun 11 07:53:48 db1 kernel: drbd0: wanted = { cs:WFConnection ro:Primary/Unknown ds:Outdated/DUnknown r--- }
そこで、無理やりprimaryにするおまじない

drbdadm -- --overwrite-data-of-peer primary all

これでOK


ha_start、ha_stop、に使っていたスクリプトも互換性の問題があり
drbdadm state all

drbdadm role all
に変更

### MEMO:また、別件だが ###
neither /etc/bind/rndc.conf nor /etc/bind/rndc.key was found
の時は rndc.confを作る
# rndc-confgen -a -r /var/cropkg/python2_5.squ
wrote key file "/etc/bind/rndc.key"

ランダムファイル渡さないと固まる
#############################

/etc/init.d/keepalivedをstart stopしてnfsサーバとbind9がきちんと動作することを確認

次はスレーブ側を立ち上げて同期させる
ーーーつづく

2009年6月10日水曜日

DRBD7からDRBD8.3.1への移行テスト(2)

前回書いている途中にもnfs-commonまわりがトラブる。

drbdはバージョンが変わって、設定ファイルが少しかわり、ディスクのメタデータも互換性がなくなっているので、パッケージ名を変えてdrbd-toolsにした。

起動後、lrp_load でパッケージを読み込もうとしたが、ネットワークの環境がことなるのでアドレスを取り直した。dhcpクライアントは udhcpcに変わっている。ボンディングを行う、ifenslaveは正常に動作していたようでbond0ができていたので、bond0にアドレスをつけ直した。

# udhcpc -i bond0
# lrp_load -b drbd-tools

drbdが自動起動して、設定ファイルの互換性が無いのでパースエラーがでる。
設定ファイルを抜き出すとこんな感じ。

resource r0 {
protocol C;
incon-degr-cmd "echo '!DRBD! pri on incon-degr' | wall ; sleep 60 ; halt -f";

startup {
wfc-timeout 180;
degr-wfc-timeout 120; # 2 minutes.
}

disk {
on-io-error pass_on;
}

net {
connect-int 10; # 10 seconds (unit = 1 second)
ping-int 10; # 10 seconds (unit = 1 second)
}

syncer {
rate 10M;
group 1;
al-extents 257;
}

on db1 {
device /dev/drbd0;
disk /dev/vg/lvother;
address 192.168.XXX.XX:7788;
meta-disk internal;
}

on db2{
device /dev/drbd0;
disk /dev/vg/lvother;
address 192.168.YYY.YY:7788;
meta-disk internal;
}
}


これの場合だと
group=1 を消す

それと
incon-degr-cmd "echo '!DRBD! pri on incon-degr' | wall ; sleep 60 ; halt -f";
の行を編集して。
handlers{
pri-on-incon-degr "echo '!DRBD! pri on incon-degr' | wall ; sleep 60 ; halt -f";
}

にする。(意味が分かっていないので自己責任で)

次に、ディスクのメタデータをアップグレードする。互換性がなくなるので、これはやらないで、新規のディスクでdrbdを動作させてデータを移行したほうがいいと思うが、これはテストなので問答無用でアップグレードする。

関係ないが、sshするので全部受信許可した
# iptables -I INPUT -j ACCEPT

drbdadmのヘルプ

# drbdadm

USAGE: drbdadm [OPTION...] [-- DRBDSETUP-OPTION...] COMMAND {all|RESOURCE...}

OPTIONS:
{--stacked|-S}
{--dry-run|-d}
{--verbose|-v}
{--config-file|-c} val
{--drbdsetup|-s} val
{--drbdmeta|-m} val
{--drbd-proxy-ctl|-p} val
{--sh-varname|-n} val
{--force|-f}

COMMANDS:
attach detach
connect disconnect
up down
primary secondary
invalidate invalidate-remote
outdate resize
syncer verify
pause-sync resume-sync
adjust wait-connect
wait-con-int role
cstate dstate
dump dump-xml
create-md show-gi
get-gi dump-md
wipe-md hidden-commands

Version: 8.3.1 (api:88)
GIT-hash: fd40f4a8f9104941537d1afc8521e584a6d3003c build by kitakura@c2duo, 2009-06-08 07:27:18

missing arguments


リソース名がr0のディスクをつくる。

# drbdadm create-md r0

--== This is a new installation of DRBD ==--
Please take part in the global DRBD usage count at http://usage.drbd.org.

The counter works anonymously. It creates a random number to identify
your machine and sends that random number, along with
DRBD's version number, to usage.drbd.org.

The benefits for you are:
* In response to your submission, the server (usage.drbd.org) will tell you
how many users before you have installed this version (8.3.1).
* With a high counter LINBIT has a strong motivation to
continue funding DRBD's development.

http://usage.drbd.org/cgi-bin/insert_usage.pl?nu=6522805258890751169&git=fd40f4a8f9104941537d1afc8521e584a6d3003c

In case you want to participate but know that this machine is firewalled,
simply issue the query string with your favorite web browser or wget.
You can control all of this by setting 'usage-count' in your drbd.conf.

* You may enter a free form comment about your machine, that gets
used on usage.drbd.org instead of the big random number.
* If you wish to opt out entirely, simply enter 'no'.
* To count this node without comment, just press [RETURN]

よく分かっていないが、インターネットにアクセスして登録っぽいことをするのか
とにかくRETURN

--== Thank you for participating in the global usage survey ==--
The server's response is:

you are the 1796th user to install this version

From now on, drbdadm will contact usage.drbd.org only when you update
DRBD or when you use 'drbdadm create-md'. Of course it will continue
to ask you for confirmation as long as 'usage-count' is at its default
value of 'ask'.

Just press [RETURN] to continue:


1796番目のユーザーらしい。さらにリターン

md_offset 5368705024
al_offset 5368672256
bm_offset 5368508416

Found ext3 filesystem which uses 5111808 kB
current configuration leaves usable 5242684 kB

Even though it looks like this would place the new meta data into
unused space, you still need to confirm, as this is only a guess.

Do you want to proceed?
[need to type 'yes' to confirm]

そして yes。

You want me to create a v08 style flexible-size internal meta data block.
There apears to be a v07 fixed-size internal meta data block
already in place on /dev/vg/lvother at byte offset 5234491392

Convert the existing v07 meta-data to v08?
[need to type 'yes' to confirm] yes

Converting meta data...
Writing meta data...
New drbd meta data block successfully created.

--== Creating metadata ==--
As with nodes, we count the total number of devices mirrored by DRBD at
at http://usage.drbd.org.

The counter works anonymously. It creates a random number to identify
the device and sends that random number, along with
DRBD's version number, to usage.drbd.org.

http://usage.drbd.org/cgi-bin/insert_usage.pl?nu=6522805258890751169&ru=8999730253493416010&rs=5368709120

* If you wish to opt out entirely, simply enter 'no'.
* To continue, just press [RETURN]
success

たぶん success

masterを落として、このアップグレードをslaveでも行う。
そして、slaveを落として masterでdrbdが起動することを確認して、
slaveを上げる予定

ーーーーつづく