例としてDebian Squeezeを使用した2つのWebサーバーのフェイルセーフアーキテクチャ

2つのサーバーからWebアプリケーションのフォールトトレランスを整理するタスクを受け取りました。 Webアプリケーションには、MySQL DBMSの静的ファイルとデータが含まれます。

顧客の主な要件は、Webアプリケーションが常に利用可能であり、5分以内に障害が発生した場合、障害を復元することです。

異なるデータセンターに地理的に分散した2台のサーバーがこの要件を満たしている必要があります。





この問題を解決するために、Debian Squeeze OSを選択しました。 Webアプリケーション開発者はDebianを使用しています。 DNSを介してフォールトトレランスロジックを実行することにしました。 ドメイン名test.ruがあります。 私の2台のサーバーはNSサーバーとして機能します。 すべてのゾーン情報はローカルに保存されます。 プライマリサーバーで障害が発生した場合、DNSは上書きされ、Aレコードはバックアップサーバーを示します。

DNSに加えて、ファイルの同期と双方向に問題がありました。 バックアップサーバーでは、メインのアイドル時間の時点で、情報をアップロードできます。 この問題を解決するには、Unisonパッケージを使用します。

MySQLデータベースを同期するには、標準のMySQLレプリケーション双方向マスターが使用されます。 単方向レプリケーション(「マスタースレーブ」)では、スレーブデータベースへの書き込み時にデータに一貫性がなく、レプリケーションエラーが発生する可能性があります。 双方向レプリケーションの場合、データベースは一貫した状態になります。

システム全体のアービターは自己記述スクリプトになります。これについては以下で説明します。



システムの準備


apache2、mysql-serverパッケージがインストールされたクリーンなDebianシステムがあります。 インターネットは情報に満ちているので、私は彼らのインストールをペイントしません。

メインサーバーはマスターIP = 10.1.0.1、バックアップサーバーはスレーブIP = 10.2.0.2です



ファイル同期


Webアプリケーションファイルは/ site / web /ディレクトリにあります。 両方のサーバーのApacheは、このディレクトリですでに構成されている必要があります。

さらに、ファイルの同期のために、サーバー間でSSHを介してパスワードなしでアクセスする必要があります。このために、キーペアを生成します。

ssh-keygen -t rsa (passphrase  ) scp /root/.ssh/id_rsa.pub root@10.2.0.2:/root/.ssh/authorized_keys2
      
      





同様に2番目のサーバーでも:

 ssh-keygen -t rsa (passphrase  ) scp /root/.ssh/id_rsa.pub root@10.1.0.1:/root/.ssh/authorized_keys2
      
      





その後、両方のサーバーの/ site / web /ディレクトリのデータが同一で​​あることを確認し、Unisonをインストールする必要があります。

 apt-get install unison
      
      





次の内容で構成ファイル/root/.unison/web.prfを作成します。

 #   ,    root = /site/web root = ssh://root@10.2.0.2//site/web #       owner = true times = true batch = true #         log = true logfile = /var/log/unison_sync.log
      
      





これで、メインサーバーで次のコマンドを使用して同期を開始できます。

 unison web
      
      





5分ごとに自動同期を行うには、次のスクリプトを使用する必要があります。

 #!/bin/sh # ,      if [ -f /var/lock/sync.lock ] then echo lockfile exists! exit 1 fi /usr/bin/touch /var/lock/sync.lock /usr/bin/unison test /bin/rm /var/lock/sync.lock #End
      
      





このスクリプトをファイル/root/bin/sync.shに保存し、実行する権利を与えます

 chmod +x /root/bin/sync.sh
      
      





そして、タスクをCRON "crontab -e"に追加します。

 */5 * * * * /root/bin/sync.sh > /dev/null 2>&1
      
      





このスクリプトを5分ごとに実行します。



MySQLレプリケーション


レプリケーションには、パスワードsome_passwordを持つMySQLレプリケーションユーザーを使用します。

メインマスターサーバーで、ファイル/etc/mysql/my.cnfを編集します。 次の行を複製セクションに貼り付けます。

 server-id = 1 log_bin = /var/log/mysql/mysql-bin.log expire_logs_days = 10 max_binlog_size = 100M binlog_ignore_db = mysql binlog_ignore_db = test master-host = 10.2.0.2 #ip-  slave- master-user = replication #   master-password = some_password #  master-port = 3306
      
      





同じファイルでbind-address変数を変更して、どのインターフェイスでもマッスルを使用できるようにします。

 bind-address = 0.0.0.0
      
      





ルートユーザーでMySQLにアクセスし、レプリケーションユーザーにバックアップスレーブサーバーからサーバーに接続する権利を与えます。

 mysql -u root -p Enter password:     root,     >grant replication slave on *.* to 'replication'@'10.2.0.2' identified by 'some_password'; >flush privileges; >quit; /etc/init.d/mysql restart
      
      







2番目のスレーブサーバーに渡します。 ファイル/etc/mysql/my.cnfを編集します。 次の行を複製セクションに貼り付けます。

 server-id = 2 log_bin = /var/log/mysql/mysql-bin.log expire_logs_days = 10 max_binlog_size = 100M binlog_ignore_db = mysql binlog_ignore_db = test master-host = 10.1.0.1 #ip-  master- master-user = replication #   master-password = some_password #  master-port = 3306
      
      





同じファイルでbind-address変数を変更して、どのインターフェイスでもマッスルを使用できるようにします。

 bind-address = 0.0.0.0
      
      





ルートとしてMySQLにアクセスし、レプリケーションユーザーにメインマスターサーバーからサーバーに接続する権利を与えます。

 mysql -u root -p Enter password:     root,     >grant replication slave on *.* to 'replication'@'10.1.0.1' identified by 'some_password'; >flush privileges; >quit; /etc/init.d/mysql restart
      
      







両方のサーバーで、レプリケーションプロセスが実行されていることを確認します。 これを行うには、次を実行します。

 #mysql —u root —p Enter password:     root,     >show slave status \G
      
      





表示される情報では、次の3つのパラメーターに関心があります。

 Slave_IO_State: Waiting for master to send event Slave_IO_Running: Yes Slave_SQL_Running: Yes
      
      





両方のサーバーで指定されたパラメーターが上記に対応する場合、すべてが正常であり、レプリケーションが構成されています。 そうでない場合は、ログを見てください。



DNSサーバー


test.ruドメインゾーンを管理するには、ドメイン設定でサーバーに委任する必要があります。 グローバルネットワーク上のサーバーに、メインサーバーのドメイン名ns.master.my.comとバックアップのドメイン名ns.slave.my.comが既にあるようにします。

DNSゾーンをサーバーに委任した後のみ、管理できます。

両方のサーバーで、bind9パッケージをインストールします。

 apt-get install bind9
      
      





/etc/bind/named.conf configに行を追加して、ゾーンを示します。

 echo 'include "/etc/bind/my-zones.conf";' >> /etc/bind/named.conf
      
      





そして、次の内容で/etc/bind/my-zones.conf構成ファイルを作成します。

 zone "test.ru" { type master; file "/etc/bind/db.test.ru"; };
      
      





最初のメインマスターサーバーで、次の内容のデータベースを/etc/bind/db.test.ruファイルに作成します。

 $ORIGIN test.ru. $TTL 10 @ IN SOA ns.master.my.com. admin.my.com. ( 2 ; Serial 10 ; Refresh 10 ; Retry 10 ; Expire 10 ) ; Negative Cache TTL IN NS ns.master.my.com. IN NS ns.slave.my.com. ; @ IN A 10.1.0.1
      
      





ns.master.my.comはこのサーバーのドメイン名です(ドメインmy.comは架空のものです)

ここでのキーはAレコードで、更新時間は10秒です。



2番目のバックアップスレーブサーバーで、次の内容のデータベースを/etc/bind/db.test.ruファイルに作成します。

 $ORIGIN test.ru. $TTL 10 @ IN SOA ns.slave.my.com. admin.my.com. ( 2 ; Serial 10 ; Refresh 10 ; Retry 10 ; Expire 10 ) ; Negative Cache TTL IN NS ns.master.my.com. IN NS ns.slave.my.com. ; @ IN A 10.1.0.1
      
      





SOAレコードの主なものとの違い。

次に、両方のサーバーでバインドをオーバーロードします

 /etc/init.d/bind9 restart
      
      





そして、外部コンピューターからtest.ruドメインのAレコードを確認しようとしています。

たとえば、nslookup test.ruを使用します。 アドレス10.1.0.1。を指定する必要があります。

何かが間違っている場合は、ログを見てください。



仲裁


最も興味深いのは、仲裁です。これにより、これらすべてが解決され、DNSゾーンが管理されます。

基礎として、SSH、DNS、HTTP、およびMySQLサービスの仕事を引き受けました。 この場合、重要なのはHTTPおよびMySQLサービスです。なぜなら、 これらのサービスの少なくとも1つがメインサーバーで機能しない場合、すべての要求をバックアップサーバーに送信する必要があり、さらに管理者に電子メールで問題を通知する必要があります。

サービスのパフォーマンスを確認するには、TELNETを使用するスクリプトを使用してポートの可用性を確認し、値を特別な状態ファイルに設定します。

最初に、メインマスターサーバーがポーリングされ、結果がファイルに記録されます。次に、バックアップスレーブサーバーがポーリングされ、結果も記録されます。 次に、メインサーバー上の重要なサービスのステータスがチェックされます。問題が発生した場合、バックアップサーバーのステータスがチェックされ、ステータスに応じてDNSゾーンが書き換えられます。 すべてのロジックを説明するのではなく、スクリプトで説明します。

まず、両方のサーバーに特別なディレクトリを作成する必要があります。

 mkdir /var/lock/sync/
      
      





すべてのスクリプトは/ root / bin /に保存されます

最初のスクリプト/root/bin/master.shは、メインマスターサーバーでDNS、SSH、HTTP、MySQLサービスをチェックします。

 #!/bin/bash #        # This script is licensed under GNU GPL version 2.0 or above # --------------------------------------------------------------------- ###     22, 53, 80  3306 ### ###         email ### ######    ###### WORKDIR="/root/bin/" SEMAFOR="/var/lock/sync/master.sem" MAILFILE="/root/bin/master_server_problem.txt" #   master- HOST="10.1.0.1" HTTP="80" SSH="22" MYSQL="3306" DNS="53" PROTOCOLS="SSH HTTP MYSQL DNS" ###  ### EMAIL="admin@my.com" ########## ############ ######       ##### ### Binaries ### TELNET=$(which telnet) ###Change dir### cd $WORKDIR ###Check if already notified### if [ -f $MAILFILE ]; then rm -rf $MAILFILE fi #     ,    if [ -f $SEMAFOR ]; then A=1 else echo "\ DNS 0 SSH 0 HTTP 0 MYSQL 0" > $SEMAFOR fi ### ### for PROTO in $PROTOCOLS do Num_PROTO=`cat $SEMAFOR | grep $PROTO | awk {'print $2'}` ( echo "quit" ) | $TELNET $HOST ${!PROTO} | grep Connected > /dev/null 2>&1 if [ "$?" -ne "1" ]; then #Ok echo "$PROTO PORT CONNECTED" if [ $Num_PROTO -ne "0" ]; then # !=0 if [ $Num_PROTO = "3" ]; then # ==3 echo "$PROTO PORT CONNECTING, AVALIBLE on server $HOST \n" >> $MAILFILE fi OLD_Line="$PROTO $Num_PROTO" NEW_Line="$PROTO 0" sed -i -e "s/$OLD_Line/$NEW_Line/g" $SEMAFOR fi else #Connection failure if [ $Num_PROTO -ne "3" ]; then if [ $Num_PROTO = "2" ]; then # ==2 send notification echo "$PROTO PORT NOT CONNECTING, FAILED on server $HOST \n" >> $MAILFILE fi OLD_Line="$PROTO $Num_PROTO" NEW_Line="$PROTO $(($Num_PROTO+1))" sed -i -e "s/$OLD_Line/$NEW_Line/g" $SEMAFOR fi fi done ###Send mail notification after 2 failed check### #   MUTT      SMTP- 10.6.6.6   #     if [ -f $MAILFILE ]; then /usr/bin/mutt -x -e "set smtp_url=smtp://10.6.6.6" -e "set from="admin@my.com"" -s "Server problem" $EMAIL < $MAILFILE fi
      
      







2番目のスクリプト/root/bin/slave.shは、バックアップスレーブサーバーのサービスをチェックします。

 #!/bin/bash #        # This script is licensed under GNU GPL version 2.0 or above # --------------------------------------------------------------------- ###     22, 53, 80  3306 ### ###         email ### ######    ###### WORKDIR="/root/bin/" SEMAFOR="/var/lock/sync/slave.sem" MAILADMIN=0 MAILFILE="/root/bin/slave_server_problem.txt" HOST="10.2.0.2" HTTP="80" SSH="22" MYSQL="3306" DNS="53" PROTOCOLS="SSH HTTP MYSQL DNS" ###  ### EMAIL="admin@my.com" ########## ######       ##### ### Binaries ### TELNET=$(which telnet) ###Change dir### cd $WORKDIR ###Check if already notified### if [ -f $MAILFILE ]; then rm -rf $MAILFILE fi if [ -f $SEMAFOR ]; then A=1 else echo "\ DNS 0 SSH 0 HTTP 0 MYSQL 0" > $SEMAFOR fi ### SSH### for PROTO in $PROTOCOLS do Num_PROTO=`cat $SEMAFOR | grep $PROTO | awk {'print $2'}` ( echo "quit" ) | $TELNET $HOST ${!PROTO} | grep Connected > /dev/null 2>&1 if [ "$?" -ne "1" ]; then #Ok echo "$PROTO PORT CONNECTED" if [ $Num_PROTO -ne "0" ]; then # !=0 if [ $Num_PROTO = "3" ]; then # ==3 echo "$PROTO PORT CONNECTING, AVALIBLE on server $HOST \n" >> $MAILFILE fi OLD_Line="$PROTO $Num_PROTO" NEW_Line="$PROTO 0" sed -i -e "s/$OLD_Line/$NEW_Line/g" $SEMAFOR fi else #Connection failure if [ $Num_PROTO -ne "3" ]; then if [ $Num_PROTO = "2" ]; then # ==2 send notification echo "$PROTO PORT NOT CONNECTING, FAILED on server $HOST \n" >> $MAILFILE fi OLD_Line="$PROTO $Num_PROTO" NEW_Line="$PROTO $(($Num_PROTO+1))" sed -i -e "s/$OLD_Line/$NEW_Line/g" $SEMAFOR fi fi done ###Send mail notification after 2 failed check### #   MUTT      SMTP- 10.6.6.6   #     if [ -f $MAILFILE ]; then /usr/bin/mutt -x -e "set smtp_url=smtp://10.6.6.6" -e "set from="admin@my.com"" -s "Server problem" $EMAIL < $MAILFILE fi
      
      





2つの変数$ HOSTと$ SEMAFORを除いて、これらの2つのファイルは同一です。

3番目のファイル/root/bin/compare.shは、サーバー上のサービスのステータスを比較し、DNSゾーンを上書きするために使用されます。

 #!/bin/bash #        # This script is licensed under GNU GPL version 2.0 or above # --------------------------------------------------------------------- FILE_MASTER="/var/lock/sync/master.sem" FILE_SLAVE="/var/lock/sync/slave.sem" HOST_MASTER="10.1.0.1" HOST_SLAVE="10.2.0.2" DNSFILE="/etc/bind/db.test.ru" LOG="/var/log/dns_rewrite.log" PROTOCOLS="HTTP MYSQL" MASTER_COL=0 SLAVE_COL=0 COL=0 for PROTO in $PROTOCOLS do COL=$(($COL + 1)) Master_PROTO=`cat $FILE_MASTER | grep $PROTO | awk {'print $2'}` MASTER_COL=$(($MASTER_COL + $Master_PROTO)) Slave_PROTO=`cat $FILE_SLAVE | grep $PROTO | awk {'print $2'}` SLAVE_COL=$(($SLAVE_COL + $Slave_PROTO)) done MAX_COL=$(($COL * 3)) if [ $MASTER_COL = $MAX_COL ]; then # ==6 if [ $SLAVE_COL = "0" ]; then #==0 #    Slave grep $HOST_MASTER $DNSFILE if [ "$?" -ne "1" ]; then #ok, rewrite sed -i -e "s/$HOST_MASTER/$HOST_SLAVE/g" $DNSFILE echo "Rewrite DNS to $HOST_SLAVE" >> $LOG /etc/init.d/bind9 restart fi fi else # check master if [ $MASTER_COL = "0" ]; then #==0 grep $HOST_SLAVE $DNSFILE if [ "$?" -ne "1" ]; then #ok, rewrite sed -i -e "s/$HOST_SLAVE/$HOST_MASTER/g" $DNSFILE echo "Rewrite DNS to $HOST_MASTER" >> $LOG /etc/init.d/bind9 restart fi else if [ $SLAVE_COL = "0" ]; then #==0 #    Slave grep $HOST_MASTER $DNSFILE if [ "$?" -ne "1" ]; then #ok, rewrite sed -i -e "s/$HOST_MASTER/$HOST_SLAVE/g" $DNSFILE echo "Rewrite DNS to $HOST_SLAVE" >> $LOG /etc/init.d/bind9 restart fi fi fi fi
      
      





最後に、これらすべてのスクリプトを1つのファイル/root/bin/dnswrite.shに収集します

 #!/bin/bash #    /root/bin/master.sh #   SLAVE /root/bin/slave.sh #       /root/bin/compare.sh
      
      





これらのスクリプトを実行する権限を追加します

 chmod +x /root/bin/*.sh
      
      





そして、crontab -eが毎分開始されるようにタスクをCRONに追加します。

 */1 * * * * /root/bin/dnswrite.sh /dev/null 2>&1
      
      







すべて準備完了です。

これで、完全に自動化されたフォールトトレラントWebアプリケーションのセットができました!

私はコメントを聞いて、コメントを受け入れてうれしいです。



All Articles