圧倒亭グランパのブログ

30年後の自分にもわかるように書くブログ

簡単極小構成でFailoverを体験してみよう! 〜 Vagrant+Chef を使って 〜 #vgadvent2013

こんにちは!@at_grandpa です。

この記事はVOYAGE GROUP エンジニアブログ : Advent Calendar 2013の19日目の記事になります。

今年ももうあと10日前後となりました。みなさんいかがお過ごしでしょうか。

 

さて、何を書こうかな

エンジニアの Advent Calendar 界隈では、「年末年始を安心して過ごすように」系記事も多数投稿されていますね。
私もそれ系を書こうと思いましたが、インフラはド素人なのでどうしたものかと。

で、以下の本を読んだ時にテーマが決まりました。

[24時間365日] サーバ/インフラを支える技術 ?スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)

[24時間365日] サーバ/インフラを支える技術 ?スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)

この冒頭に、簡単なFailoverの仕組みが書いてありました。ですので今回は、簡単極小構成で実際にFailoverを体験してみようと思います。

 「Failoverって言葉は知っているけど、実際どういうことが行われているの?」

という方は、簡単ですのでぜひやってみてください。

 

簡単極小構成

構成は以下です。物理的に必要なのはMacのみです。物理サーバーもケーブルも何もいりません。良い世の中ですね。お財布にも優しいです。

f:id:at_grandpa:20131219092927p:plain

 

Vagrant と Chef については、以下のサイトが優しく解説されていると思います。

今っぽい Vagrant + Chef Solo チュートリアル

Berkshelfの記事の直前まで読めば、今回の構成は実行できるかと思います。

 

その他、個人的にVagtrantのインストール方法などもまとめたので、以下もご参照ください。

Vagrant のインストールから仮想環境立ち上げまでを、オプションなどと共にちょっと細かく追ってみた

  

Failoverの流れ

では実際にどう処理が流れていくのかを見てみましょう。

 

実運用中はこのような動作をしています。Terminal.appではpingコマンドで接続を常に確認します。 server1とserver2の間では、お互いにヘルスチェックをします。↓

f:id:at_grandpa:20131219092933p:plain

 

そして、ひとつのサーバー(server2)を潰します。すると、server1の health check が障害を検知し、Failoverの処理が走ります。↓

f:id:at_grandpa:20131219092941p:plain

 

server1によるIPアドレスの引き継ぎと、引き継ぎの報告がなされます。↓

f:id:at_grandpa:20131219092946p:plain

 

無事、両IP共、接続が可能となります。↓

f:id:at_grandpa:20131219092951p:plain

たった一つのコマンドで環境構築

今回の環境を構築するには、Vagrantfile、recipe、シェルスクリプトの3つを編集します。1つずつ見ていきましょう。

Vagrantfile

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  # common setting
  config.vm.box = "cent_os"

  # server1
  config.vm.define "server1" do |server|
    server.vm.network :private_network, ip: "192.168.1.11"
  end

  # server2
  config.vm.define "server2" do |server|
    server.vm.network :private_network, ip: "192.168.1.12"
  end

  # chef-solo
  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "./chef-repo/site-cookbooks"
    # 自作の[httpd]というcookbookを作成し、ここで指定(後述)
    chef.add_recipe "httpd"
  end

  # provision :shell
  config.vm.provision :shell do |sh|
    sh.path = "failover.sh"
  end

end
  • OSはcentos
  • server1とserver2のそれぞれのプライベートIPアドレスを指定(VIPではなく)
  • provisionとは、Vagrantでマシンを立ち上げた際に実行する処理
  • :chef-solo:マシン立ち上げと同時に、iptablesã‚„apacheのインストールをする
  • :shell:failover用のスクリプト

 

クックブック "httpd" のrecipe

# cookbook:httpd

service "iptables" do
    action [:stop, :disable]
end

package "httpd" do
    action :install
end

service "httpd" do
    action [ :enable, :start ]
end

template "/var/www/html/index.html" do
    source "index.html.erb"
    mode 0644
end
  • iptablesのストップ
  • apacheのインストール
  • apacheの起動
  • /var/www/html/index.html に、クライアント側にある index.html.erb を転送  (これでVIPの接続をブラウザからチェックできる)

 

failover.sh

#!/bin/sh
VIP="192.168.1.20 192.168.1.21"
DEV="eth0"

healthcheck() {
    # VIPのループを回る
    for i in $VIP;do
        # 自分のVIP以外の場合はtrueで中の処理へ
        if [ -z "`ip addr show $DEV | grep $i`" ]; then
            # レスポンスを格納
            RES=`curl -s -I http://${i}/| head -n 1 | cut -f 2 -d ' '`
            # レスポンスが何もない場合
            if [ -z "${RES}" ]; then
                CIP="$i"
                return 1
            fi
            # レスポンスはあるが200以外の時
            if [ "200" -ne "${RES}" ]; then
                CIP="$i"
                return 1
            fi
        fi
    done
    return 0
}

ip_takeover() {
    # 障害のあったVIPを自分のサーバーに加える
    ip addr add $CIP/24 dev $DEV
    # arpを送信(IPを引き継いだことを報告)
    arping -A -I $DEV -c 1 $CIP
}

# 1秒間隔で health check
while healthcheck; do
    echo "health ok!"
    sleep 1
done

# failover処理を実行
echo "failover!"
ip_takeover

 

このシェルスクリプトは、1回目の実行と2回目の実行で動作が異なります。

failover.shの1回目実行の動作

  • まずはChef Soloによってserver1でスクリプトが走る
  • server1にて、healthcheck()が走る
  • 192.168.1.20のIPは存在しないのでhealthcheck()を抜け、ip_takeover()が実行される
  • server1にVIP : 192.168.1.20 が付与される
  • 次に立ち上がったserver2でスクリプトが走る
  • server2にて、healthcheck()が走る
  • 192.168.1.20のIPは存在しているので(server1)レスポンスが返ってくるため、次の 192.168.1.21 をチェックする
  • 192.168.1.21のIPは存在しないのでhealthcheck()を抜け、ip_takeover()が実行される
  • server2にVIP : 192.168.1.21 が付与される

これで、2つのVIPのサーバーが生きている状態になりました。

 

failover.shの2回目の実行の動作

  • server1でもう一度、手動で実行した場合を考える
  • for文内のifæ–‡if [ -z "ip addr show $DEV | grep $i" ]; thenは、自分のIP以外はtrueとなるので、レスポンスの有無をチェックするのは常に相手(server2)のIPとなる
  • これで、server1は「server2の health check をしている状態」になる
  • server2の場合はIPが逆になるだけで、全く同じ動き
  • これでお互いにhealth checkしている
  • 常に1秒おきに health check をし、相手の反応がなかったら、相手のIPを自分に引き継ぐ

さて、いよいよサーバーを立ち上げます。

 

vagrant up !!

必要なコマンドは、vagrant up のみです。 Vagrantfileのあるディレクトリに移動して、コマンドを実行しましょう。

実行結果↓

Bringing machine 'server1' up with 'virtualbox' provider...
Bringing machine 'server2' up with 'virtualbox' provider...
[server1] Importing base box 'cent_os'...

... 【省略】サーバー立ち上げのもろもろの処理 ...

[server1] The guest additions on this VM do not match the installed version of
VirtualBox! In most cases this is fine, but in rare cases it can
cause things such as shared folders to not work properly. If you see
shared folder errors, please update the guest additions within the
virtual machine and reload your VM.

Guest Additions Version: 4.1.18
VirtualBox Version: 4.2     ← ■■■ サーバーが立ち上がった ■■■
[server1] Configuring and enabling network interfaces...
[server1] Mounting shared folders...
[server1] -- /vagrant
[server1] -- /tmp/vagrant-chef-1/chef-solo-1/cookbooks
[server1] Running provisioner: chef_solo...       ← ■■■ ここからchefの実行 ■■■
Generating chef JSON and uploading...
load: 1.67  cmd: VBoxManage 1401 waiting 0.04u 0.03s
Running chef-solo...
[2013-12-19T01:54:03+01:00] INFO: *** Chef 10.12.0 ***
[2013-12-19T01:54:04+01:00] INFO: Setting the run_list to ["recipe[httpd]"] from JSON
[2013-12-19T01:54:04+01:00] INFO: Run List is [recipe[httpd]]
[2013-12-19T01:54:04+01:00] INFO: Run List expands to [httpd]
[2013-12-19T01:54:04+01:00] INFO: Starting Chef Run for localhost
[2013-12-19T01:54:04+01:00] INFO: Running start handlers
[2013-12-19T01:54:04+01:00] INFO: Start handlers complete.
[2013-12-19T01:54:04+01:00] INFO: service[iptables] stopped     ← ■■■ iptables ■■■
[2013-12-19T01:54:04+01:00] INFO: service[iptables] disabled
[2013-12-19T01:54:34+01:00] INFO: package[httpd] installing httpd-2.2.15-29.el6.centos from base repository      ← ■■■ apacheインストール ■■■
[2013-12-19T01:54:42+01:00] INFO: package[httpd] installed version 2.2.15-29.el6.centos
[2013-12-19T01:54:42+01:00] INFO: service[httpd] enabled
[2013-12-19T01:54:42+01:00] INFO: service[httpd] started      ← ■■■ apache起動 ■■■
[2013-12-19T01:54:42+01:00] INFO: template[/var/www/html/index.html] mode changed to 644      ← ■■■ index.htmlの設置 ■■■
[2013-12-19T01:54:42+01:00] INFO: template[/var/www/html/index.html] updated content
[2013-12-19T01:54:42+01:00] INFO: Chef Run complete in 38.139665 seconds
[2013-12-19T01:54:42+01:00] INFO: Running report handlers
[2013-12-19T01:54:42+01:00] INFO: Report handlers complete
[server1] Running provisioner: shell...      ← ■■■ シェルの実行 ■■■
[server1] Running: /var/folders/q5/c85lqfy57t7f1sw5lsh6nzn4wnj0pf/T/vagrant-shell20131219-1258-162e2hz
failover!      ← ■■■ ip_takeover()の実行 ■■■
ARPING 192.168.1.20 from 192.168.1.20 eth0      ← ■■■ VIPの付与 ■■■
Sent 1 probes (1 broadcast(s))
Received 0 response(s)

... 【省略】同様の動作をserver2でも実行 ...
  • centosを立ち上げる(この時、プライベートIPを付与)
  • 立ち上がったら Chef Solo を実行
  • iptables停止、apacheのインストール&起動、index.htmlの生成
  • 指定したシェルスクリプトの実行(failover.sh)→ VIPの付与

このような流れになっています。

立ち上がると、Terminal.appからの2つのpingが通るようになります。

 

health check の起動

vagrant upだけでは、まだ1回しかfailover.shは実行されていないので、VIPの付与は終わっていますが health check は動作していません。 vagrant ssh server1を打ち、server1内でシェルスクリプトを実行しましょう。

  • vagrant ssh server1
  • [vagrant@localhost ~]$ ← server1に入ることができる
  • sudo sh /vagrant/failover.sh ← Vagrantfileと同じディレクトリ内のものは、この共有ディレクトリとリンクしているので、failover.shが存在する
  • health check 実行完了
  • server2も同様に

 

Let's 障害発動

障害を起こしましょう。こんな経験あまりないですよ。おもいっきり障害を起こしてください。

では、sever2を停止しましょう。コマンドはクライアント上でvagrant halt server2です。

停止したあとのTerminal.appでのping結果は以下です。↓

f:id:at_grandpa:20131219112424p:plain

 

Failover!帰ってきた192.168.1.21!

 

無事以下の状態になっていますね。

f:id:at_grandpa:20131219092951p:plain

 

server2は殉職しましたが、サービスは継続しています。( ゚Д゚ノノ"☆パチパチパチパチ

 

課題

この構成は、簡単が故にまだまだ課題が残っています。

  • 3台あったらどうなるの?
  • シェルスクリプトをいちいち起動しないといけない
  • 一回のfailoverしか対応していない

などなどです。それらを解決するために、ロードバランサなどの機器が用いられています。

詳しくはここでは割愛しますが、[24時間365日] サーバ/インフラを支える技術 ?スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ) に書かれていますので、まだの方はご一読をば。

 

最後に

知識として知ってはいても、実際に自分で動かしてみると、これまた違った知識として定着しますね。

やはり手を動かすことは大切!みなさんもぜひ体験してみてください。

 

明日のAdventCalendarは、我らがアイドル@saya_223nです。 アイドルっぽい記事で心を癒してくれることでしょう!